/********** Hasbani Linux Device Driver Source File ********/
/********** <      USB Section      >               ********/
/********** Author          :  Paul Rayman          ********/
/********** Release Version :  1.0                  ********/
/****************************************************************************
*
*	Name:			hsbusb.c
*
*	Description:	 E2 Linux Driver implementation File.
*
*	Copyright:		(c) 1999 Conexant Systems Inc.
*
*	Revision:	Date:		Author:				Description:
*
*	Linux-1.1	May 29 2002	Shen Wei Wang			Modified.
*					shenwei.wang@conexant.com
*
*****************************************************************************
*	$Author:   Shen Wei Wang  $
*	$Revision:   1.1  $
*	$Modtime:   May 29 2002 11:09:58  $
****************************************************************************/

#include "hasbani.h"
#include "ntdef.h"
//#include "usb-uhci.h"
#include <asm/io.h>


#ifdef	USB_DEBUG
#define	PDEBUG(arg...)	printk(arg)
#else
#define  PDEBUG(arg...)
#endif


#define HSB_USB_MAX_DEVICES	1
#define HSB_USB_MINOR_BASE	32

/***********************************************************
Euphrates ROM: 0xcafe
Rushmore ROM: 0xcafd
Proposed OF ROM: 0xcafc
Euphrates P52: 0xcafe
Euphrates Rushmore: 0xcb00
Thames (P52, no Rushmore): 0xcaff
Hasbani - 0xCB01
K2 - VID: 0x045E PID: 0x006C
E2 P52: 0xCB05
E2 Rushmore: 0xCB06
************************************************************/

#define HSB_USB_VENDOR_ID	0x0572
#define HSB_USB_VENDOR_K2	0x045e

#define HSB_USB_PRODUCT_ID_HASBANI 0xcb01
#define HSB_USB_PRODUCT_ID_K2	0x006c
#define HSB_USB_PRODUCT_ID_E2	0xcb05
#define HSB_USB_PRODUCT_ID_RM	0xcb06


#define HSB_USB_RECV_SIZE	4096	//Size of USB recv buffer

#define HSB_USB_DATA_IN		0x82
#define HSB_USB_DATA_OUT	0x2

struct hsb_usb_device* adsl_devices[HSB_USB_MAX_DEVICES];

static int hsb_usb_datapid;

DECLARE_WAIT_QUEUE_HEAD(hsb_usb_wqh);

static char hsb_usb_driver_name[]="HasbaniAdslDriver";

/*********Added April 25th**********/
static int hsb_usb_hardware_present=0;

/***********Management***********/
int hsb_usb_dld_start(struct hsb_usb_device* phsb_device);
void hsb_usb_dld_stop(struct hsb_usb_device* phsb_device);

void hsb_usb_dld_conti_read(purb_t urb);
void hsb_usb_dld_conti_send(purb_t purb);

struct sk_buff* hsb_usb_dld_read_data();
int hsb_usb_dld_send_data(void* dat, int length);

/**********File operations****/
int hsb_usb_fopen(struct inode* inode,struct file* file);
int hsb_usb_frelease(struct inode* inode,struct file* file);
int hsb_usb_fioctl(struct inode* inode, struct file* file, unsigned int command, unsigned long param);

static struct file_operations hsb_usb_fops=
{
open:	hsb_usb_fopen,
release: 	hsb_usb_frelease,
ioctl:	hsb_usb_fioctl,
};

/*******USB Driver entries ***/
int hsb_usb_processqueue_thread(void* param);
void hsb_usb_processqueue(struct hsb_usb_device* );

void * hsb_usb_probe(struct usb_device*,unsigned int,const struct usb_device_id*);
void hsb_usb_disconnect(struct usb_device*, void *);
int hsb_usb_ioctl(struct usb_device*, unsigned int,void *);

void hsb_usb_conti_read(purb_t purb);
void hsb_usb_conti_send(purb_t purb);
void hsb_usb_stop2(struct hsb_usb_device * phsb_device);

static struct usb_driver hsb_usb_driver_desc=
{
name:		hsb_usb_driver_name,
probe: 		hsb_usb_probe,
disconnect:	hsb_usb_disconnect,
ioctl:		hsb_usb_ioctl,
minor:		HSB_USB_MINOR_BASE,
fops:		&hsb_usb_fops,
}; 


extern int hsb_snd_buf_index;
extern int hsb_eth_rcv_pool_index;

extern int hsb_usb_cmdrcvpid;
extern int hsb_usb_cmd_process_packet_thread(void* param);

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

extern BOOLEAN
BlDelay
(
	IN	ULONG	uInterval	/* milliseconds */ 
);
/***************Management**************************/
struct hsb_usb_dld_data_struct
{
	int length;
	unsigned char * data;
};


#define HSB_USB_DLD_IN		0x81
#define HSB_USB_DLD_OUT		0x01

#define HSB_USB_DLD_BUFF_SIZE	64

static struct sk_buff_head hsb_usb_dld_recv_queue_head;
static struct sk_buff_head hsb_usb_dld_send_queue_head;

static purb_t hsb_usb_dld_recv_urb;
static purb_t hsb_usb_dld_send_urb;

static char *hsb_usb_dld_recv_buff;
DECLARE_WAIT_QUEUE_HEAD(hsb_usb_dld_wqh);

static int hsb_usb_dld_send_queue_empty=1;


/*------------------------------------------*/
void hsb_usb_dld_conti_read(purb_t urb)
{
	struct sk_buff* skb;
	struct hsb_usb_device* phsb_device=(struct hsb_usb_device*)urb->context;
	if(!phsb_device)
		return ;
	
	/*Added April 25*/
	if(!hsb_usb_hardware_present)
		return;
		
	switch(urb->status)
	{
	case 0:
		skb=dev_alloc_skb(urb->actual_length);
		if(!skb)
		{
			return;
		}
		
		memcpy(skb_put(skb,urb->actual_length),urb->transfer_buffer,urb->actual_length);
		skb_queue_tail(&hsb_usb_dld_recv_queue_head,skb);
		wake_up(&hsb_usb_dld_wqh);
		break;
	default:
		return;
	}
	FILL_BULK_URB(urb,
			phsb_device->pdev,
			usb_rcvbulkpipe(phsb_device->pdev,HSB_USB_DLD_IN),
			urb->transfer_buffer,
			HSB_USB_DLD_BUFF_SIZE,
			(usb_complete_t)hsb_usb_dld_conti_read,
			phsb_device
			);
	urb->transfer_flags|=USB_QUEUE_BULK;
	usb_submit_urb(urb);	
}
void hsb_usb_dld_conti_send(purb_t purb)
{
	struct hsb_usb_device* phsb_device=
		(struct hsb_usb_device*)purb->context;
	struct sk_buff * skb;
	/*Added April 25*/
	if(!hsb_usb_hardware_present)
		return;

	skb=skb_dequeue(&hsb_usb_dld_send_queue_head);

	if(skb)
		kfree_skb(skb);
	if(skb_queue_empty(&hsb_usb_dld_send_queue_head))
	{
		hsb_usb_dld_send_queue_empty=1;
		return;
	}
	skb=skb_peek(&hsb_usb_dld_send_queue_head);
	FILL_BULK_URB(purb,
			phsb_device->pdev,
			usb_sndbulkpipe(phsb_device->pdev,HSB_USB_DLD_OUT),
			skb->data,
			skb->len,
			(usb_complete_t)hsb_usb_dld_conti_send,
			phsb_device
			);
	purb->transfer_flags|=USB_QUEUE_BULK;
	usb_submit_urb(purb);
}	
int hsb_usb_dld_start(struct hsb_usb_device* phsb_device)
{
	if(!phsb_device)
		return -ENODEV;

	/*Added April 25*/
	if(!hsb_usb_hardware_present)
		return -ENODEV;
		
	//Allocate URB:
	if((hsb_usb_dld_recv_urb=usb_alloc_urb(0))==0)
	{
		return -ENOMEM;
	}
	if((hsb_usb_dld_send_urb=usb_alloc_urb(0))==0)
	{
		return -ENOMEM;
	}

	//Allocate receive buffer
	if((hsb_usb_dld_recv_buff=kmalloc(HSB_USB_DLD_BUFF_SIZE,GFP_KERNEL))==0)
	{
		return -ENOMEM;
	}

	//Init queue head:
	skb_queue_head_init(&hsb_usb_dld_recv_queue_head);
	skb_queue_head_init(&hsb_usb_dld_send_queue_head);

	//Submit the first read request:
	FILL_BULK_URB(hsb_usb_dld_recv_urb,
			phsb_device->pdev,
			usb_rcvbulkpipe(phsb_device->pdev,HSB_USB_DLD_IN),
			hsb_usb_dld_recv_buff,
			HSB_USB_DLD_BUFF_SIZE,
			(usb_complete_t)hsb_usb_dld_conti_read,
			phsb_device
			);
	hsb_usb_dld_recv_urb->transfer_flags|=USB_QUEUE_BULK;
	return usb_submit_urb(hsb_usb_dld_recv_urb);
}

void hsb_usb_dld_stop(struct hsb_usb_device* phsb_device)
{
	//Unlink & free URBs.
	if((hsb_usb_dld_recv_urb)&&hsb_usb_dld_recv_urb->status==-EINPROGRESS)
	{
		usb_unlink_urb(hsb_usb_dld_recv_urb);
	}
	if(hsb_usb_dld_recv_urb)
	{
		usb_free_urb(hsb_usb_dld_recv_urb);
		hsb_usb_dld_recv_urb=0;
	}
	if((hsb_usb_dld_send_urb)&&hsb_usb_dld_send_urb->status==-EINPROGRESS)
	{
		usb_unlink_urb(hsb_usb_dld_send_urb);
	}
	if(hsb_usb_dld_send_urb)
	{
		usb_free_urb(hsb_usb_dld_send_urb);
		hsb_usb_dld_send_urb=0;
	}
	//Free receive buffer
	if(hsb_usb_dld_recv_buff)
	{
		kfree(hsb_usb_dld_recv_buff);
		hsb_usb_dld_recv_buff=0;
	}
	//Empty send/receive queue.
	while(!skb_queue_empty(&hsb_usb_dld_recv_queue_head))
	{
		kfree_skb(skb_dequeue(&hsb_usb_dld_recv_queue_head));
	}
	while(!skb_queue_empty(&hsb_usb_dld_send_queue_head))
	{
		kfree_skb(skb_dequeue(&hsb_usb_dld_send_queue_head));
	}

}

struct sk_buff* hsb_usb_dld_read_data()
{
	if(skb_queue_empty(&hsb_usb_dld_recv_queue_head))	
	{
		interruptible_sleep_on(&hsb_usb_dld_wqh);
		if(signal_pending(current))
			return 0;
	}
	return skb_dequeue(&hsb_usb_dld_recv_queue_head);
	
}

int hsb_usb_dld_send_data(void* dat, int length)
{
	struct hsb_usb_device * phsb_device;

	struct sk_buff* skb;

	if(adsl_devices[0]==0)
		return -ENODEV;

	/*Added April 25*/
	if(!hsb_usb_hardware_present)
		return -ENODEV;
		
	skb=dev_alloc_skb(length);
	if(!skb)
		return -ENOMEM;

	memcpy(skb_put(skb,length),dat,length);


	phsb_device=adsl_devices[0];

	hsb_usb_dld_send_queue_empty=0;

	if(!skb_queue_empty(&hsb_usb_dld_send_queue_head))
	{
		skb_queue_tail(&hsb_usb_dld_send_queue_head,skb);
		return 0;
	}
	/* send queue is not empty*/
	skb_queue_tail(&hsb_usb_dld_send_queue_head,skb);
	FILL_BULK_URB(hsb_usb_dld_send_urb,
			phsb_device->pdev,
			usb_sndbulkpipe(phsb_device->pdev,HSB_USB_DLD_OUT),
			skb->data,
			skb->len,
			(usb_complete_t)hsb_usb_dld_conti_send,
			phsb_device
			);
	hsb_usb_dld_send_urb->transfer_flags|=USB_QUEUE_BULK;
	return usb_submit_urb(hsb_usb_dld_send_urb);
}
/***************File operations**************************/
int hsb_usb_fopen(struct inode* inode,struct file* file)
{
	return 0;
}
int hsb_usb_frelease(struct inode* inode,struct file* file)
{
	return 0;
}
int hsb_usb_fioctl(struct inode* inode, struct file* file, 
		unsigned int command, unsigned long param)
{	struct sk_buff* skb;
	char * user_buff=(char*)param,*recv_buff;
	int i,packetcount=0;
	purb_t urb;
//	struct hsb_usb_dld_data_struct  dld_data_struct,*pstruct;

	PDEBUG("USB driver: processing ioctl command %d\n",command);

	if(!adsl_devices[0])
	{
		PDEBUG("USB_FIOCTL: no device found.\n");
		return 0;
	}
	
	switch(command)
	{
	case 0:
		return hsb_usb_start(adsl_devices[0]->hsb_eth_dev);
	case 0xff:
		hsb_usb_stop(adsl_devices[0]->hsb_eth_dev);
		return 0;
	case 1:	/*read*/
		if(skb_queue_empty(&(adsl_devices[0]->recv_queue_head)))
			return 0;
		skb=skb_dequeue(&(adsl_devices[0]->recv_queue_head));
		for(i=0;i<skb->len;i++)
			put_user(skb->data[i],user_buff+i);
		return i;
	case 2:/*send*/
		skb=dev_alloc_skb(HSB_USB_DLD_BUFF_SIZE);
		if(!skb)
			return -1;
		for(i=0;i<HSB_USB_DLD_BUFF_SIZE;i++)
		{
			get_user(skb->data[i],user_buff+i);
		}
		skb_put(skb,HSB_USB_DLD_BUFF_SIZE);
		return hsb_usb_send(adsl_devices[0]->hsb_eth_dev,skb);
	case 3:/*recv queue dump*/
		printk("\n\n\nReceive queue dump:\n");
		while((skb=skb_dequeue(&adsl_devices[0]->recv_queue_head))!=0)
		{
			packetcount++;
			printk("\nPacket #%d size:%d\nData:\n",packetcount,skb->len);
			for(i=0;i<skb->len;i++)
			{
				printk("%2x ",skb->data[i]);
			}
		}
		return 0;
	case 4:/*send queue dump*/
		printk("\n\n\nSend queue dump:\n");
		while((skb=skb_dequeue(&adsl_devices[0]->send_queue_head))!=0)
		{
			packetcount++;
			printk("\nPacket #%d size:%d\nData:\n",packetcount,skb->len);
			for(i=0;i<skb->len;i++)
			{
				printk("%2x ",skb->data[i]);
			}
		}
		return 0;
	case 10:/*pseudo recv*/
		if(!adsl_devices[0]->started)
		{
			PDEBUG("USB_FIOCTL: start device first!\n");
			return -ENODEV;
		}

		urb=adsl_devices[0]->recv_context.purb;

		if(urb->status==-EINPROGRESS)
		{
			usb_unlink_urb(urb);
		}
		recv_buff=(char*)urb->transfer_buffer;
		if(!recv_buff)
		{
			PDEBUG("USB_FIOCTL: receive URB has no buffer\n");
			return -EINVAL;
		}

		for(i=0;i<HSB_USB_DLD_BUFF_SIZE;i++)
		{
			get_user(recv_buff[i],user_buff+i);
		}
		urb->actual_length=i;
		urb->status=0;
		PDEBUG("\n\n\nUSB_FIOCTL: calling usb_conti_read...\n");
		hsb_usb_conti_read(urb);
		return 0;
	case 30://Starting download:
		return 0;//hsb_usb_dld_start(adsl_devices[0]);
	case 39://Stopping download:
		//hsb_usb_dld_stop(adsl_devices[0]);
		return 0;
/*	case 31://Download send:
		pstruct =(struct hsb_usb_dld_data_struct*)param;

		get_user(dld_data_struct.length,&(pstruct->length));
		get_user(dld_data_struct.data,&(pstruct->data));

		PDEBUG("Length=%d\n",dld_data_struct.length);

		recv_buff=kmalloc(dld_data_struct.length,GFP_KERNEL);
		if(!recv_buff)
			return -ENOMEM;

		for(i=0;i<dld_data_struct.length;i++)
		{
			get_user(recv_buff[i],dld_data_struct.data+i);
		}

		i=hsb_usb_dld_send_data(recv_buff,dld_data_struct.length);

		kfree(recv_buff);

		
		return 0;		

	case 32://Download read:
		skb=hsb_usb_dld_read_data();
		if(!skb)
			return 0;
		for(i=0;i<skb->len;i++)
			put_user(skb->data[i],user_buff+i);
		return i;
		
	case 35://Query download send queue emtpy:
		return hsb_usb_dld_send_queue_empty;
	case 40://Set MAC address:
		return 0;
*/		
	//Add by Shen Wei Wang 4.27
	case 50:
		PDEBUG("Number is 50\n" );
		if(adsl_devices[0]->DevType == DEV_HASBANI)
			return 0;
		BlFirmwareInitialize (adsl_devices[0]);
		return 0;
				
	}
	return 0;
}

/*********USB driver entries**************/

extern wait_queue_head_t hsb_usb_cmd_wqh;
void * hsb_usb_probe(struct usb_device* dev ,unsigned int interface ,const struct usb_device_id* id_table)
{
	int i;
	DEV_TYPE devtype;
	char cDevType[80];

/*	if(dev->descriptor.idVendor!=HSB_USB_VENDOR_ID||
	   dev->descriptor.idProduct!=HSB_USB_PRODUCT_ID_E2)
		return 0;
*/
	
/*	if(dev->descriptor.idVendor!=HSB_USB_VENDOR_ID)
		return 0;
	if(dev->descriptor.idProduct=HSB_USB_PRODUCT_ID_E2)
		devtype=DEV_E2;
	else if(dev->descriptor.idProduct=HSB_USB_PRODUCT_ID_K2)
		devtype=DEV_K2;
	else if(dev->descriptor.idProduct=HSB_USB_PRODUCT_ID_HASBANI)
		devtype=DEV_HASBANI;
	else
		return 0;
*/	

	if( dev->descriptor.idVendor == HSB_USB_CUSTOMER_VID &&
		dev->descriptor.idProduct == HSB_USB_CUSTOMER_PID )
	{
		devtype = HSB_USB_CUSTOMER_DEVTYPE;
		strcpy(cDevType, "Adsl Modem");
		goto CustomerAdsl;
	}
	if(dev->descriptor.idVendor != HSB_USB_VENDOR_ID &&
		dev->descriptor.idVendor != HSB_USB_VENDOR_K2 )
		return 0;
			
	switch(dev->descriptor.idProduct)
	{
	case HSB_USB_PRODUCT_ID_RM:
		devtype=DEV_E2RM;
		strcpy(cDevType, "E2 Rushmore");
		break;
	case HSB_USB_PRODUCT_ID_E2:
		devtype=DEV_E2;
		strcpy(cDevType, "E2");
		break;
	case HSB_USB_PRODUCT_ID_K2:
		devtype=DEV_K2;
		strcpy(cDevType, "K2");
		break;
		
	case HSB_USB_PRODUCT_ID_HASBANI:
		devtype=DEV_HASBANI;
		strcpy(cDevType, "Hasbani or Rushmore");
		break;
	default:
		return 0;
	}
		
CustomerAdsl:
	for(i=0;i<HSB_USB_MAX_DEVICES;i++)
		if(adsl_devices[i]==NULL)
			break;
	if(i>=HSB_USB_MAX_DEVICES)
	{
		PDEBUG("No more entry for this device, sorry!\n");
		return 0;
	}
	adsl_devices[i]=kmalloc(sizeof(struct hsb_usb_device),GFP_KERNEL);
	if(!adsl_devices[i])
	{
		return 0;
	}

	memset(adsl_devices[i],0,sizeof(struct hsb_usb_device));

	skb_queue_head_init(&(adsl_devices[i]->send_queue_head));
	skb_queue_head_init(&(adsl_devices[i]->recv_queue_head));
	adsl_devices[i]->pdev=dev;
	adsl_devices[i]->minor=i;
	
	//Added by Shen Wei Wang 2002.5.20
	adsl_devices[i]->DevType = devtype;
	adsl_devices[i]->bSetMac = 0;
	adsl_devices[i]->bDownload = 0;
	adsl_devices[i]->bSoftBoot = 0;
	adsl_devices[i]->dldsndurb = 0;
	adsl_devices[i]->bDldError = 0;
	init_waitqueue_head(&adsl_devices[i]->iscomplete);
	skb_queue_head_init(&adsl_devices[i]->cmd_recv_queue_head);
	
	spin_lock_init (&adsl_devices[i]->sndqlock);
  	spin_lock_init (&adsl_devices[i]->recvqlock);
  	
  	spin_lock_init (&adsl_devices[i]->cmdrecvqlock);
	if((adsl_devices[i]->dldsndurb=usb_alloc_urb(0))==0)
	{
		return 0;
	}
	if((adsl_devices[i]->dldrcvurb=usb_alloc_urb(0))==0)
	{
		return 0;
	}
	//End

	hsb_eth_startdevice(adsl_devices[i]);

	printk("%s connected, minor number: %d\n", cDevType, i+HSB_USB_MINOR_BASE);

	MOD_INC_USE_COUNT;
	usb_inc_dev_use( dev );
	/*Added April 25********/
	hsb_usb_hardware_present=1;
//	init_waitqueue_head (&adsl_devices[i]->iscomplete);
	wake_up(&hsb_usb_cmd_wqh);
	return adsl_devices[i];
}

void hsb_usb_stop2(struct hsb_usb_device * phsb_device)
{
//	struct hsb_usb_device * phsb_device;
	struct sk_buff* pskb;
	/*Added April 25*/
	if(!hsb_usb_hardware_present)
		return;

//	phsb_device=((struct hsb_eth_priv_data*)(dev->priv))->usb_dev;

	if(phsb_device==NULL)
		return;
	if(phsb_device->started==0)
		return;
	/* Set flag*/
	phsb_device->started=0;

//	phsb_device->bDownload = 0;
	
	
	
	/* Unlink URBs, free URBs*/
	if(phsb_device->recv_context.purb&&phsb_device->recv_context.purb->status==-EINPROGRESS)
	{
		usb_unlink_urb(phsb_device->recv_context.purb);
		usb_free_urb(phsb_device->recv_context.purb);
		PDEBUG("receive URB freed\n");
	}
	if(phsb_device->send_context.purb&&phsb_device->send_context.purb->status==-EINPROGRESS)
	{
		usb_unlink_urb(phsb_device->send_context.purb);
		usb_free_urb(phsb_device->send_context.purb);
		PDEBUG("send URB freed\n");
	}
	/* Free recv_context.skb*/
	if(phsb_device->recv_context.skb)
	{
		dev_kfree_skb(phsb_device->recv_context.skb);
		PDEBUG("receive skb freed\n");
	}
	/* Clear send & recv queue*/
	while(!skb_queue_empty(&phsb_device->send_queue_head))
	{
		pskb=skb_dequeue(&(phsb_device->send_queue_head));
		dev_kfree_skb(pskb);
	}
	
	while(!skb_queue_empty(&phsb_device->recv_queue_head))
	{
		pskb=skb_dequeue(&(phsb_device->recv_queue_head));
		dev_kfree_skb(pskb);
	}

	PDEBUG("USB device %d stopped!\n",phsb_device->minor+HSB_USB_MINOR_BASE);
}

void hsb_usb_disconnect(struct usb_device* dev, void * param)
{
	int minor=((struct hsb_usb_device*)param)->minor;

//	if((adsl_devices[minor]->DevType != DEV_HASBANI) && adsl_devices[minor]->bDownload)
		hsb_usb_cmd_stop(adsl_devices[minor]);
		
	hsb_eth_stopdevice(adsl_devices[minor]);

//	hsb_usb_stop2(adsl_devices[minor]);
	kfree(adsl_devices[minor]);
	adsl_devices[minor]=0;
	
	MOD_DEC_USE_COUNT;
	/*Added April 25****/
	usb_dec_dev_use ( dev );
	hsb_usb_hardware_present=0;
	if(hsb_usb_cmdrcvpid != 0)	
	    wake_up(&hsb_usb_cmd_wqh);
}

int hsb_usb_ioctl(struct usb_device* dev,unsigned int command, void *param)
{
	return 0;
}

int hsb_usb_start(struct net_device * dev)
{
	/*Added April 25*/
	struct hsb_usb_device* phsb_device;
	struct hsb_usb_context * recv_context, *send_context;
	if(!hsb_usb_hardware_present)
		return -ENODEV;
	
	phsb_device=((struct hsb_eth_priv_data*)(dev->priv))->usb_dev;
	recv_context=&(phsb_device->recv_context);
	send_context=&(phsb_device->send_context);
	if(phsb_device->started)
		return 0;


	
	recv_context->purb=usb_alloc_urb(0);
	send_context->purb=usb_alloc_urb(0);

	if(send_context->purb==NULL)
		return -ENOMEM;
	if(recv_context->purb==NULL)
		return -ENOMEM;

	recv_context->skb=dev_alloc_skb(HSB_USB_RECV_SIZE);	

	if(recv_context->skb==NULL)
	{
		usb_free_urb(recv_context->purb);
		usb_free_urb(send_context->purb);
		return -ENOMEM;
	}
	
	FILL_BULK_URB(recv_context->purb,
			phsb_device->pdev,
			usb_rcvbulkpipe(phsb_device->pdev,HSB_USB_DATA_IN),
			recv_context->skb->data,
			HSB_USB_RECV_SIZE,
			(usb_complete_t)hsb_usb_conti_read,
			phsb_device
			);
	recv_context->purb->transfer_flags|=USB_QUEUE_BULK;
	
	
	usb_submit_urb(recv_context->purb);
//	init_waitqueue_head (&phsb_device->iscomplete);	
	phsb_device->started=1;

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

	PDEBUG("USB device %d started!\n",phsb_device->minor);
	return 0;
}

void hsb_usb_stop(struct net_device* dev)
{
	struct hsb_usb_device * phsb_device;
	struct sk_buff* pskb;
	
	unsigned long iflags;
	/*Added April 25*/
	if(!hsb_usb_hardware_present)
		return;

	phsb_device=((struct hsb_eth_priv_data*)(dev->priv))->usb_dev;

	if(phsb_device==NULL)
		return;
	if(phsb_device->started==0)
		return;
	/* Set flag*/
	phsb_device->started=0;

	/* Unlink URBs, free URBs*/
	if(phsb_device->recv_context.purb&&phsb_device->recv_context.purb->status==-EINPROGRESS)
	{
		usb_unlink_urb(phsb_device->recv_context.purb);
		usb_free_urb(phsb_device->recv_context.purb);
		PDEBUG("receive URB freed\n");
	}
	if(phsb_device->send_context.purb&&phsb_device->send_context.purb->status==-EINPROGRESS)
	{
		usb_unlink_urb(phsb_device->send_context.purb);
		usb_free_urb(phsb_device->send_context.purb);
		PDEBUG("send URB freed\n");
	}
	/* Free recv_context.skb*/
	if(phsb_device->recv_context.skb)
	{
		dev_kfree_skb(phsb_device->recv_context.skb);
		PDEBUG("receive skb freed\n");
	}
	/* Clear send & recv queue*/
	
	spin_lock_irqsave(&phsb_device->sndqlock, iflags);
	while(!skb_queue_empty(&phsb_device->send_queue_head))
	{
		pskb=skb_dequeue(&(phsb_device->send_queue_head));
		dev_kfree_skb(pskb);
	}
	spin_unlock_irqrestore(&phsb_device->sndqlock, iflags);
	
	spin_lock_irqsave(&phsb_device->recvqlock, iflags);
	while(!skb_queue_empty(&phsb_device->recv_queue_head))
	{
		pskb=skb_dequeue(&(phsb_device->recv_queue_head));
		dev_kfree_skb(pskb);
	}
	spin_unlock_irqrestore(&phsb_device->recvqlock, iflags);
	
/**********************************
unsigned long iflags;
spin_lock_irqsave(&phsb_device->sndqlock, iflags);
spin_unlock_irqrestore(&phsb_device->sndqlock, iflags);

spin_lock_irqsave(&phsb_device->recvqlock, iflags);
spin_unlock_irqrestore(&phsb_device->recvqlock, iflags);
****************************************/


//Cleaning.....
hsb_snd_buf_index=0;
hsb_eth_rcv_pool_index=0;
//end
	PDEBUG("USB device %d stopped!\n",phsb_device->minor+HSB_USB_MINOR_BASE);
}

void hsb_usb_conti_read(purb_t purb)
{	

	unsigned long iflags;

	struct hsb_usb_device* phsb_device=
		(struct hsb_usb_device*)purb->context;
	struct sk_buff* skb;

	/* Added April 25*/
	if(!hsb_usb_hardware_present)
		return;

/*
{
uhci_t *uhci;
unsigned int io_addr;

unsigned short  status;

if(phsb_device->pdev->bus&&phsb_device->pdev->bus->hcpriv)
{
         uhci = phsb_device->pdev->bus->hcpriv;
        io_addr = uhci->io_addr;


	status = inw (io_addr + USBSTS);
	if(status==2)
	{
		printk("status=3\n");
		printk("purb->status=%d, purb->actual_length=%d\n", purb->status, purb->actual_length);
	}
}
}
*/


	switch(purb->status)
	{
	case 0:			/*a successful read*/
		skb=dev_alloc_skb(purb->actual_length);
		if(!skb)
		{
			PDEBUG("No more memory to accomodate more URB, read stopped!\n");
			
			return;
		}

		memcpy(skb_put(skb,purb->actual_length),purb->transfer_buffer,purb->actual_length);
		spin_lock_irqsave(&phsb_device->recvqlock, iflags);
		skb_queue_tail(&phsb_device->recv_queue_head,skb);
		spin_unlock_irqrestore(&phsb_device->recvqlock, iflags);
		PDEBUG("\n\nUSB_CONTI_READ: Received 0x%x bytes of data from USB, queued...okay.\n",purb->actual_length);
	#ifdef USB_DEBUG
	printk("USB_Read_conti:.....................skb->len=%d\n", skb->len);
	hsb_skb_dump(skb);
	printk("USB_Read_conti: End..................................\n");
	#endif

		wake_up(&hsb_usb_wqh);

		break;
	case -EPIPE:
		usb_clear_halt(phsb_device->pdev,usb_rcvbulkpipe(phsb_device->pdev,HSB_USB_DATA_IN));
		break;
	case -ENOENT:
	case -EILSEQ:
	case -ETIMEDOUT:
		PDEBUG("USB_CONTI_READ: read loop stopped!\n");
		
		return;
	default:
		PDEBUG("Error receiving data: %d",purb->status);
		PDEBUG("USB_CONTI_READ: read loop stopped!\n");
		
		return;
	}
	FILL_BULK_URB(purb,
			phsb_device->pdev,
			usb_rcvbulkpipe(phsb_device->pdev,HSB_USB_DATA_IN),
			phsb_device->recv_context.skb->data,
			HSB_USB_RECV_SIZE,
			(usb_complete_t)hsb_usb_conti_read,
			phsb_device
			);
	purb->transfer_flags|=USB_QUEUE_BULK;
	purb->status = 0;

	
	usb_submit_urb(purb);
	
	
}		

int hsb_usb_send(struct net_device* dev, struct sk_buff* skb)
{
	
	unsigned long iflags;

	struct hsb_usb_device* phsb_device=((struct hsb_eth_priv_data*)(dev->priv))->usb_dev;
	int err;


	
	/* Added April 25*/
	if(!hsb_usb_hardware_present)
		return -ENODEV;
	
	if(!phsb_device->started)
		return -ENODEV;

		
	if(!skb_queue_empty(&(phsb_device->send_queue_head)))
	{
		spin_lock_irqsave(&phsb_device->sndqlock, iflags);
		skb_queue_tail(&(phsb_device->send_queue_head),skb);
		spin_unlock_irqrestore(&phsb_device->sndqlock, iflags);
		return 0;
	}
	
	#ifdef USB_DEBUG
	printk("USB_SEND:.................................skb->len=0x%x\n", skb->len);
	hsb_skb_dump(skb);
	printk("................................................\n");
	#endif
	/* send queue is not empty*/
	
	
/**********************************
unsigned long iflags;
spin_lock_irqsave(&phsb_device->sndqlock, iflags);
spin_unlock_irqrestore(&phsb_device->sndqlock, iflags);

spin_lock_irqsave(&phsb_device->recvqlock, iflags);
spin_unlock_irqrestore(&phsb_device->recvqlock, iflags);
****************************************/

	spin_lock_irqsave(&phsb_device->sndqlock, iflags);
	skb_queue_tail(&(phsb_device->send_queue_head),skb);
	spin_unlock_irqrestore(&phsb_device->sndqlock, iflags);
	
	
	FILL_BULK_URB(phsb_device->send_context.purb,
			phsb_device->pdev,
			usb_sndbulkpipe(phsb_device->pdev,HSB_USB_DATA_OUT),
			skb->data,
			skb->len,
			(usb_complete_t)hsb_usb_conti_send,
			phsb_device
			);
	phsb_device->send_context.purb->transfer_flags|=USB_QUEUE_BULK;
	

		
	err=usb_submit_urb(phsb_device->send_context.purb);

  	if (err != 0)
    		skb_unlink(skb);
    			
 	
 	
	PDEBUG("USB_SEND: data submitted\n");

	
	return err;
}

void hsb_usb_conti_send(purb_t purb)
{
	spinlock_t usb_lock = SPIN_LOCK_UNLOCKED;
	unsigned long iflags;
	
	struct hsb_usb_device* phsb_device=
		(struct hsb_usb_device*)purb->context;
	struct sk_buff * skb;


	/* Added April 25*/
	if(!hsb_usb_hardware_present)
		return;
	spin_lock_irqsave(&phsb_device->sndqlock, iflags);	
	skb=skb_dequeue(&(phsb_device->send_queue_head));
	spin_unlock_irqrestore(&phsb_device->sndqlock, iflags);
	
	if(skb)
		kfree_skb(skb);

	if(skb_queue_empty(&(phsb_device->send_queue_head)))
	{	
		netif_wake_queue(phsb_device->hsb_eth_dev);
		return;
	}	
	spin_lock_irqsave(&phsb_device->sndqlock, iflags);

/**********************************
unsigned long iflags;
spin_lock_irqsave(&phsb_device->sndqlock, iflags);
spin_unlock_irqrestore(&phsb_device->sndqlock, iflags);

spin_lock_irqsave(&phsb_device->recvqlock, iflags);
spin_unlock_irqrestore(&phsb_device->recvqlock, iflags);
****************************************/
	
	skb=skb_peek(&(phsb_device->send_queue_head));
	spin_unlock_irqrestore(&phsb_device->sndqlock, iflags);
	
	FILL_BULK_URB(purb,
			phsb_device->pdev,
			usb_sndbulkpipe(phsb_device->pdev,HSB_USB_DATA_OUT),
			skb->data,
			skb->len,
			(usb_complete_t)hsb_usb_conti_send,
			phsb_device
			);
	purb->transfer_flags|=USB_QUEUE_BULK;
//	usb_submit_urb(purb);
	#ifdef USB_DEBUG
	printk("USB_SEND CONTI:...........................skb->len=0x%x\n", skb->len);
	hsb_skb_dump(skb);
	printk("................................................\n");
	#endif	
 	usb_submit_urb(purb);
	
 	
	PDEBUG("USB_CONTI_SEND: data submitted\n");
}


//extern int hsb_usb_cmdrcvpid;
//extern int hsb_usb_cmd_process_packet_thread(void* param);
void hsb_usb_startdaemon()
{
	hsb_usb_datapid=kernel_thread(hsb_usb_processqueue_thread,0,
			CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
			
			
	hsb_usb_cmdrcvpid=kernel_thread(hsb_usb_cmd_process_packet_thread,0,0);
}

extern void hsb_usb_stop_cmddaemon();
void hsb_usb_stopdaemon()
{
	int ret;
	ret=kill_proc(hsb_usb_datapid,SIGTERM,1);
	if(!ret)
	{
		int count= 10*100;
		while(hsb_usb_datapid&&--count)
		{
			current->state=TASK_INTERRUPTIBLE;
			schedule_timeout(1);
		}
		if(!count)
			err("Giving up on killing USBReadThread.\n");
	}
	
	hsb_usb_stop_cmddaemon();
}

int hsb_usb_processqueue_thread(void* param)
{
	int i=0;
	DECLARE_WAITQUEUE(wait,current);

	lock_kernel();
	exit_files(current);
	daemonize();

	strcpy(current->comm,"USBDataReadD");

	unlock_kernel();
	add_wait_queue(&hsb_usb_wqh,&wait);

	while(1)
	{
		interruptible_sleep_on(&hsb_usb_wqh);
		if(signal_pending(current))
				break;
		
		
		for(i=0;i<HSB_USB_MAX_DEVICES;i++)
			if(adsl_devices[i])
				hsb_usb_processqueue(adsl_devices[i]);
	}
	
	hsb_usb_datapid=0;
	return 0;
}
void hsb_usb_processqueue(struct hsb_usb_device* phsb_device)
{
	struct sk_buff* new;
	unsigned long iflags;
	
	
	if(skb_queue_empty(&(phsb_device->recv_queue_head)))
		return;

	
	spin_lock_irqsave(&phsb_device->recvqlock, iflags);
	
	while((new=skb_dequeue(&(phsb_device->recv_queue_head)))!=NULL)
	{
		spin_unlock_irqrestore(&phsb_device->recvqlock, iflags);
		
		hsb_eth_rx(phsb_device->hsb_eth_dev,new);
		
		spin_lock_irqsave(&phsb_device->recvqlock, iflags);
	}
	spin_unlock_irqrestore(&phsb_device->recvqlock, iflags);
}

int init_module()
{
	int i;

	for(i=0;i<HSB_USB_MAX_DEVICES;i++)
		adsl_devices[i]=0;

	hsb_usb_startdaemon();

	BlDelay(20);
	return usb_register(&hsb_usb_driver_desc);

}
void cleanup_module()
{
	usb_deregister(&hsb_usb_driver_desc);
	
	hsb_usb_stopdaemon();
}



