/****************************************************************************
*
*	Name:			SndRcv.c
*
*	Description:	E2 command sending and receiving part implementation file.
*
*	Copyright:		(c) 1999 Conexant Systems Inc.
*
*	Revision:	Date:		Author:				Description:
*
*	Linux-1.0	May 29 2002	Shen Wei Wang			Created.
*					shenwei.wang@conexant.com
*
*****************************************************************************
*	$Author:   Shen Wei Wang  $
*	$Revision:   1.0  $
*	$Modtime:   May 29 2002 11:09:58  $
****************************************************************************/

#include "hasbani.h"
#include <linux/vmalloc.h> 
#include <linux/slab.h>
#include <linux/fs.h>

#include <linux/kmod.h>

#include "ntdef.h"
#include "BlDefs.h"
#include "FwInit.h"
#include "usbsndcm.h"
#include "SndRcv.h"

extern struct hsb_usb_device* adsl_devices[1];
extern int hsb_usb_hardware_present;
extern int udsl_print_packet (const unsigned char *data, int len);

extern BOOLEAN FwImageFileLoad
(
	PCWSTR	pwszFileName, 
	DWORD**	ppdwFwImage, 
	ULONG*	pulFwImageSize
);

extern unsigned char BlFirmwareInitialize
(
	struct hsb_usb_device*	DeviceObject
);

unsigned char bPackingMode = 0;

static int orgfsuid,orgfsgid;
static mm_segment_t orgfs;
int SendFileToBoard = 0;

#define HSB_USB_MAX_DEVICES	1

#define HSB_USB_CMD_IN		0x81
#define HSB_USB_CMD_OUT		0x01

#define HSB_USB_PACKET_COUNT	16
#define HSB_USB_CMD_BUFF_SIZE	64*HSB_USB_PACKET_COUNT
#define BUFFERSIZE		4096*4


//static struct sk_buff_head hsb_usb_cmd_recv_queue_head;
static struct sk_buff_head hsb_usb_cmd_send_queue_head;

//static purb_t hsb_usb_cmd_recv_urb;
static purb_t hsb_usb_cmd_send_urb;

static char *hsb_usb_cmd_recv_buff;
//DECLARE_WAIT_QUEUE_HEAD(hsb_usb_cmd_wqh);
wait_queue_head_t hsb_usb_cmd_wqh;

static int hsb_usb_cmd_send_queue_empty=1;
int hsb_usb_cmdrcvpid=0;
//static BOOLEAN bUpLoaded = TRUE;

void hsb_usb_cmd_conti_send(purb_t purb);
void hsb_usb_cmd_conti_read(purb_t urb);
int hsb_usb_cmd_send(void* dat, int length);
struct sk_buff* hsb_usb_cmd_read();
void hsb_usb_cmd_stop(struct hsb_usb_device* phsb_device);
int hsb_usb_cmd_init(struct hsb_usb_device* phsb_device);

int hsb_usb_process_packet();
int hsb_usb_cmd_process_packet_thread(void* param);



BOOLEAN CmdSendWithUid
(
	IN PDEVICE_OBJECT	DeviceObject,
	IN	CM_REQUEST_CODE	eCommand, 
	IN	ADDRESS_TYPE	uId
);

BOOLEAN CmdSendReadMem
(
	IN PDEVICE_OBJECT	DeviceObject,
	IN DWORD		dwAddress,
	IN DWORD		dwCount
);

struct file * OpenConfigFile(char * FileName);
BOOLEAN CloseConfigFile(struct file *hFile);
BOOLEAN WriteToConfigFile( struct file *hFile, BYTE * pBuf, ULONG len );

BOOLEAN InitReadContext( MemoryReadContext ** pCon, DWORD dwAddr, DWORD dwBytes );
int SaveMemoryToFile( struct file * hFile,
	MemoryReadContext * pCon, CM_PACKET * pCmd  );
int SendFileSystemToMemory( struct hsb_usb_device* DeviceObject, char * FileName );
int SendFirmwareToMemory( struct hsb_usb_device* DeviceObject, char * FileName,
	 DWORD dwAddress, DWORD dwSize );
NTSTATUS FillMemory( IN PDEVICE_OBJECT	DeviceObject,
    DWORD dwAddress, DWORD uSize, UCHAR   value,
    BL_MEMORY_ACCESS uAccessType);
    	
	
#if ( 1 )//DBG_ENABLED_DBG_CM_REQUEST_CODE)

#define DBG_CM_REQUEST_CODE(e)								\
(											\
	(										\
		((e) >= CM_REQUEST_UNDEFINED) &&					\
		((e) <= CM_REQUEST_MAX)							\
	) ? 										\
		(g_DbgCmRequestCode[(e) - CM_REQUEST_UNDEFINED]) :			\
		(DBG_ENUM_IS_OUT_OF_RANGE)						\
)															\


extern CHAR*	g_DbgCmRequestCode[];

#else /* (DBG_ENABLED_DBG_CM_REQUEST_CODE) */ 

#define DBG_CM_REQUEST_CODE(pszName, pCardData)

#endif /* (DBG_ENABLED_DBG_CM_REQUEST_CODE) */ 

#if ( 0 )
int g_MemAllocCount = 0;
int g_MemFreeCount = 0;
#define MALLOCINC() 						\
(								\
	 printk("Malloc Number=%d\n", g_MemAllocCount++ )	\
)

#define MFREEINC()						\
(								\
 	printk("MFree Number=%d\n", g_MemFreeCount++ )		\
)

#else
#define MALLOCINC()
#define MFREEINC()

#endif




int hsb_usb_cmd_send(void* dat, int length)
{
	
	spinlock_t usb_lock = SPIN_LOCK_UNLOCKED;
	unsigned long flags;
	int err;
	
	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_cmd_send_queue_empty=0;

	if(!skb_queue_empty(&hsb_usb_cmd_send_queue_head))
	{
		skb_queue_tail(&hsb_usb_cmd_send_queue_head,skb);
		return 0;
	}
	/* send queue is not empty*/
	spin_lock_irqsave(&usb_lock, flags);
	
	skb_queue_tail(&hsb_usb_cmd_send_queue_head,skb);
	FILL_BULK_URB(hsb_usb_cmd_send_urb,
			phsb_device->pdev,
			usb_sndbulkpipe(phsb_device->pdev,HSB_USB_CMD_OUT),
			skb->data,
			skb->len,
			(usb_complete_t)hsb_usb_cmd_conti_send,
			phsb_device
			);
	hsb_usb_cmd_send_urb->transfer_flags|=USB_QUEUE_BULK;
	err = usb_submit_urb(hsb_usb_cmd_send_urb);
	spin_unlock_irqrestore(&usb_lock, flags);
	
	return err;
}
extern BOOLEAN
BlDatagramReceive
(
    IN	PDEVICE_OBJECT DeviceObject,
    IN 	PVOID		pDatagram,
    IN	ULONG		uSize
);

BOOLEAN hsb_usb_cmd_soc_ready(struct hsb_usb_device* phsb_device)
{
    CM_PACKET Cmd;
    unsigned char ret = 0;
    
    DBG_PRINT00("Entering...\n");
        
    if(BlDatagramReceive(phsb_device, &Cmd, sizeof(CM_PACKET)))
    {
	DBG_PRINT00("uRequest=0x%x, State=0x%x\n", 
	    Cmd.uRequest, Cmd.Data.Array[0]);
	    
	if(Cmd.uRequest == 0x1)
	    BlDatagramReceive(phsb_device, &Cmd, sizeof(CM_PACKET));
	
	if((Cmd.uRequest == CM_REQUEST_CARD_GET_DATA_LINK_STATUS))//&&
//	    ((END_DEVICE_STATE)Cmd.Data.Array[0] == END_DEVICE_READY))
	    
	    ret = 1;
	else
	    ret = 0;
	
	DBG_PRINT00("uRequest=0x%x, State=0x%x\n",
	    Cmd.uRequest, Cmd.Data.Array[0]);	    
	    
    }
        
    DBG_PRINT00("soc_ready: Leaving ret=%d\n", ret);

    return ret;
}

BOOLEAN hsb_usb_cmd_hsb_reboot(struct hsb_usb_device* phsb_device)
{
    CM_PACKET Cmd;
    unsigned char ret = 0;
    
    DBG_PRINT00("Entering...\n");
        
    if(BlDatagramReceive(phsb_device, &Cmd, sizeof(CM_PACKET)))
    {
	DBG_PRINT00("uRequest=0x%x, State=0x%x\n", 
	    Cmd.uRequest, Cmd.Data.Array[0]);
	    
/*	while(Cmd.uRequest != CM_REQUEST_CARD_GET_DATA_LINK_STATUS);
	{
	    BlDelay(10);
	    if(signal_pending(current))
		return 1;
	    memset(&Cmd, 0, sizeof(CM_PACKET));
	    if(BlDatagramReceive(phsb_device, &Cmd, sizeof(CM_PACKET)))
	    	return 0;
	}
*/
//	if((Cmd.uRequest == CM_REQUEST_CARD_GET_DATA_LINK_STATUS))//&&
//	    ((END_DEVICE_STATE)Cmd.Data.Array[0] == END_DEVICE_READY))
	if (Cmd.uStatus != 0)	    
	    ret = 1;
	else
	    ret = 0;
	
	DBG_PRINT00("uRequest=0x%x, uStatus=0x%x, State=0x%x\n",
	    Cmd.uRequest, Cmd.uStatus, Cmd.Data.Array[0]);	    
	    
    }
        
    DBG_PRINT00("soc_ready: Leaving ret=%d\n", ret);

    return ret;
}



void hsb_usb_cmd_conti_read(purb_t urb)
{
	
	unsigned long iflags;
	
	int i;
	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;
	DBG_PRINT00 ( "Entering!\n");	
	
	switch(urb->status)
	{
	case 0:
		if ( urb->actual_length < 64 )
			break;
		for( i=0; i<urb->actual_length/64; i++)
		{
		skb=dev_alloc_skb(64);//urb->actual_length);
		MALLOCINC();
		//MFREEINC();
		
		if(!skb)
		{
			
			return;
		}


/**********************************
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);
****************************************/		
		//memcpy(skb_put(skb,urb->actual_length),urb->transfer_buffer,urb->actual_length);
		memcpy(skb_put(skb, 64), urb->transfer_buffer+i*64, 64 );
		spin_lock_irqsave(&phsb_device->cmdrecvqlock, iflags);
		skb_queue_tail(&phsb_device->cmd_recv_queue_head,skb);
		spin_unlock_irqrestore(&phsb_device->cmdrecvqlock, iflags);
		}
	
		memset ( urb->transfer_buffer, 0, HSB_USB_CMD_BUFF_SIZE );
		wake_up(&hsb_usb_cmd_wqh);
		break;
	case -EPIPE:
		usb_clear_halt(phsb_device->pdev,usb_rcvbulkpipe(phsb_device->pdev,HSB_USB_CMD_IN));
		break;
	case -ENOENT:
	case -EILSEQ:
	case -ETIMEDOUT:
		PDEBUG("USB_CMD_READ: read loop stopped!\n");
		
		return;
	default:
		PDEBUG("Error receiving data: %d",urb->status);
		PDEBUG("USB_CMD_READ: read loop stopped!\n");
		
		return;		
		
	}
	FILL_BULK_URB(urb,
			phsb_device->pdev,
			usb_rcvbulkpipe(phsb_device->pdev,HSB_USB_CMD_IN),
			urb->transfer_buffer,
			HSB_USB_CMD_BUFF_SIZE,
			(usb_complete_t)hsb_usb_cmd_conti_read,
			phsb_device
			);
	urb->transfer_flags|=USB_QUEUE_BULK;
	usb_submit_urb(urb);	
	
	
	DBG_PRINT00 ( "Leaving!\n");
}
void hsb_usb_cmd_conti_send(purb_t purb)
{
	spinlock_t usb_lock = SPIN_LOCK_UNLOCKED;
	unsigned long flags;	
	
	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_cmd_send_queue_head);

	if(skb)
		kfree_skb(skb);
	if(skb_queue_empty(&hsb_usb_cmd_send_queue_head))
	{
		hsb_usb_cmd_send_queue_empty=1;
		return;
	}
	spin_lock_irqsave(&usb_lock, flags);
	skb=skb_peek(&hsb_usb_cmd_send_queue_head);
	FILL_BULK_URB(purb,
			phsb_device->pdev,
			usb_sndbulkpipe(phsb_device->pdev,HSB_USB_CMD_OUT),
			skb->data,
			skb->len,
			(usb_complete_t)hsb_usb_cmd_conti_send,
			phsb_device
			);
	purb->transfer_flags|=USB_QUEUE_BULK;
	usb_submit_urb(purb);
	spin_unlock_irqrestore(&usb_lock, flags);
}

int hsb_usb_cmd_init(struct hsb_usb_device* phsb_device)
{
	
	spinlock_t usb_lock = SPIN_LOCK_UNLOCKED;
	unsigned long flags;
	int err;	
	
	if(!phsb_device)
		return -ENODEV;

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


	//Allocate receive buffer
	if((hsb_usb_cmd_recv_buff=kmalloc(HSB_USB_CMD_BUFF_SIZE,GFP_KERNEL))==0)
	{
		return -ENOMEM;
	}
	MALLOCINC();
	//MFREEINC();

	DBG_PRINT00 ( "Cmd Daemon Thread Will Be Started!\n");


	//Submit the first read request:
	FILL_BULK_URB(phsb_device->dldrcvurb,
			phsb_device->pdev,
			usb_rcvbulkpipe(phsb_device->pdev,HSB_USB_CMD_IN),
			hsb_usb_cmd_recv_buff,
			HSB_USB_CMD_BUFF_SIZE,
			(usb_complete_t)hsb_usb_cmd_conti_read,
			phsb_device
			);
	phsb_device->dldrcvurb->transfer_flags|=USB_QUEUE_BULK;
	
	spin_lock_irqsave(&usb_lock, flags);
	err = usb_submit_urb(phsb_device->dldrcvurb);
	spin_unlock_irqrestore(&usb_lock, flags);
	DBG_PRINT00 ( "usb_submit_urb err= %d!\n", err);
	return err;
}

void hsb_usb_stop_cmddaemon()
{
	int ret;
	int count= 10*100;
	if ( hsb_usb_cmdrcvpid <= 0 )
		return;
	ret=kill_proc(hsb_usb_cmdrcvpid,SIGTERM,1);
	if(!ret)
	{
		
		while(hsb_usb_cmdrcvpid&&--count)
		{
			current->state=TASK_INTERRUPTIBLE;
			schedule_timeout(1);
		}
		if(!count)
			err("Giving up on killing USBCmdThread.\n");
	}
	
	if ( count )
		DBG_PRINT00("killed\n");
}
void hsb_usb_cmd_stop(struct hsb_usb_device* phsb_device)
{
	unsigned long iflags;
	phsb_device->bDldError = 1;
	
	//Stop the cmd daemon
//	hsb_usb_stop_cmddaemon();
	
	//Unlink & free URBs.
/*	if((hsb_usb_cmd_recv_urb)&&hsb_usb_cmd_recv_urb->status==-EINPROGRESS)
	{
		usb_unlink_urb(hsb_usb_cmd_recv_urb);
	}
	if(hsb_usb_cmd_recv_urb)
	{
		usb_free_urb(hsb_usb_cmd_recv_urb);
		hsb_usb_cmd_recv_urb=0;
	}

	//MALLOCINC();
	MFREEINC();
*/

	if((phsb_device->dldrcvurb)&&phsb_device->dldrcvurb->status==-EINPROGRESS)
	{
		usb_unlink_urb(phsb_device->dldrcvurb);
	}

	if(phsb_device->dldrcvurb)
	{
		usb_free_urb(phsb_device->dldrcvurb);
		phsb_device->dldrcvurb=0;		
	}
	
	if((phsb_device->dldsndurb)&&phsb_device->dldsndurb->status==-EINPROGRESS)
	{
		usb_unlink_urb(phsb_device->dldsndurb);
	}

	if(phsb_device->dldsndurb)
	{
		usb_free_urb(phsb_device->dldsndurb);
		phsb_device->dldsndurb=0;		
	}
	//Free receive buffer
	if(hsb_usb_cmd_recv_buff)
	{
		kfree(hsb_usb_cmd_recv_buff);
		hsb_usb_cmd_recv_buff=0;
	}
	//MALLOCINC();
	MFREEINC();

	
	//Empty send/receive queue.

	spin_lock_irqsave(&phsb_device->cmdrecvqlock, iflags);
		
	while(!skb_queue_empty(&phsb_device->cmd_recv_queue_head))
	{
		kfree_skb(skb_dequeue(&phsb_device->cmd_recv_queue_head));
		//MALLOCINC();
		MFREEINC();		
	}
	
	spin_unlock_irqrestore(&phsb_device->cmdrecvqlock, iflags);
//	while(!skb_queue_empty(&hsb_usb_cmd_send_queue_head))
//	{
//		kfree_skb(skb_dequeue(&hsb_usb_cmd_send_queue_head));
//	}
	

}

void hsb_usb_cmd_reboot_stop(struct hsb_usb_device* phsb_device)
{
	unsigned long iflags;

	if((phsb_device->dldrcvurb)&&phsb_device->dldrcvurb->status==-EINPROGRESS)
	{
		usb_unlink_urb(phsb_device->dldrcvurb);
	}

	if(phsb_device->dldrcvurb)
	{
		usb_free_urb(phsb_device->dldrcvurb);
		phsb_device->dldrcvurb=0;		
	}
	
/*	if((phsb_device->dldsndurb)&&phsb_device->dldsndurb->status==-EINPROGRESS)
	{
		usb_unlink_urb(phsb_device->dldsndurb);
	}

	if(phsb_device->dldsndurb)
	{
		usb_free_urb(phsb_device->dldsndurb);
		phsb_device->dldsndurb=0;		
	}
*/
	//Free receive buffer
	if(hsb_usb_cmd_recv_buff)
	{
		kfree(hsb_usb_cmd_recv_buff);
		hsb_usb_cmd_recv_buff=0;
	}
	//MALLOCINC();
	MFREEINC();

	
	//Empty send/receive queue.

	spin_lock_irqsave(&phsb_device->cmdrecvqlock, iflags);
		
	while(!skb_queue_empty(&phsb_device->cmd_recv_queue_head))
	{
		kfree_skb(skb_dequeue(&phsb_device->cmd_recv_queue_head));
		//MALLOCINC();
		MFREEINC();		
	}
	
	spin_unlock_irqrestore(&phsb_device->cmdrecvqlock, iflags);

}


int hsb_usb_cmd_process_packet_thread(void* param)
{
//	struct sk_buff* skb;
	int i=0;
	int err = 0;
	DECLARE_WAITQUEUE(wait,current);
	struct hsb_usb_device * phsb_device=(struct hsb_usb_device*)param;
	
//	if (!phsb_device )
//		return 0;

	lock_kernel();
	exit_mm(current);
	exit_files(current);
	exit_fs(current);
	daemonize();

	strcpy(current->comm,"CmdRD");
	
//	unlock_kernel();
	
	init_waitqueue_head(&hsb_usb_cmd_wqh);
	add_wait_queue(&hsb_usb_cmd_wqh,&wait);

	
LoopBegin: i = 0;
	   err = 0;

/*	while (!adsl_devices[0])
	{
		BlDelay ( 5*1000 );
		if(signal_pending(current))
			goto back;
	}
	phsb_device = adsl_devices[0];
	if ( phsb_device->DevType == DEV_HASBANI )
	{
		BlDelay ( 10*1000 );
		if(signal_pending(current))
			goto back;		
		goto LoopBegin;
	}
*/
	interruptible_sleep_on(&hsb_usb_cmd_wqh);
	if(signal_pending(current))
		goto back;
	phsb_device = adsl_devices[0];
	if (!phsb_device)
	    goto LoopBegin;
	if ( phsb_device->DevType == DEV_HASBANI )
		goto HasbaniBegin;
	
	while ( !CmCommandSend( phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 ))
	{
		BlDelay ( 3*1000 );
		if(signal_pending(current))
			goto back;
	}
	BlDelay(1);
	if(hsb_usb_cmd_soc_ready( phsb_device ))
	    goto HasbaniBegin;
//	while ( i < 3 )
//	{
//		i++;
//		if (err = BlFirmwareInitialize ( phsb_device ))
//			break;	
//	}
	err = BlFirmwareInitialize ( phsb_device );
	if (err != 1 )
	{
		phsb_device = NULL;
		goto LoopBegin;
	}	

	if ( !hsb_usb_hardware_present )
		goto LoopBegin;
//	if ( phsb_device->DevType != DEV_E2RM )	
		CmCommandSend(phsb_device, 	CM_REQUEST_CHIP_ADSL_LINE_START, 0);
	BlDelay (4*1000);
	
	if(signal_pending(current))
		goto back;
	
	if ( !hsb_usb_hardware_present )
	{
		phsb_device = NULL;
		goto LoopBegin;
	}	
HasbaniBegin:	BlDelay ( 20 );
	hsb_usb_cmd_init ( phsb_device );
//	phsb_device->bDownload = 1;
//	BlDelay ( 4*1000 );
	
	err = CmCommandSend( phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );
	if( ( phsb_device->DevType == DEV_HASBANI ) && (0!=err) )
	{
		bPackingMode = 0;
		phsb_device->bSetMac = TRUE;

	}
//	else
//		CmCommandSend( phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );
//	BlDelay(10);
//	err = CmCommandSend( phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );
	while(hsb_usb_hardware_present)
	{
		err = 0;
		interruptible_sleep_on(&hsb_usb_cmd_wqh);
		if(signal_pending(current))
			goto back;
		
//		for(i=0;i<HSB_USB_MAX_DEVICES;i++)
//			if(adsl_devices[i])
		err = hsb_usb_process_packet(phsb_device);
		if ( -1 == err )
			goto back;
		if ( 1 == err )
			break;	
	}
	if( ( phsb_device->DevType == DEV_HASBANI ) && (1==err) )
	{
		hsb_usb_cmd_reboot_stop(phsb_device);
		while(hsb_usb_hardware_present)
		{
//			err = CmCommandSend( phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );
			err = CmCommandSend( phsb_device, CM_REQUEST_PACKING_MODE, bPackingMode );
			BlDelay(9*1000);
			if(signal_pending(current))
				goto back;
			
		}

//		interruptible_sleep_on(&hsb_usb_cmd_wqh);
//		if(signal_pending(current))
//			goto back;		
	}

//	phsb_device = 0;
	goto LoopBegin;
	
back:	hsb_usb_cmdrcvpid=0;
	return 0;
}


char cnxtuserapp_path[256] = "/usr/local/e2/dhcpcd";

static int exec_cnxtuserapp(void * if_name)
{
	static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
	char *argv[] = { cnxtuserapp_path, "-n", (char*)if_name, NULL };
	int ret;

	ret = call_usermodehelper(cnxtuserapp_path, argv, envp);
	if (ret) {
		printk(KERN_ERR
		       "E2 ADSL: failed to exec %s -n %s, errno = %d\n",
		       cnxtuserapp_path, (char*) if_name, ret);
	}
	return ret;
}

extern void hsb_eth_set_mac_addr(struct hsb_usb_device* usb_dev, char* mac,int len);
//extern int hsb_eth_open_soft(struct net_device *dev);
//extern int hsb_eth_stop_soft(struct net_device* dev);
/******************************
return value:
	-1  Error or shutdown signal received.
	0   Normal
	1   Device is hasbani.
	
*****************************/

int hsb_usb_process_packet(struct hsb_usb_device* phsb_device)
{
	struct sk_buff* skb;
	unsigned long iflags;

//	static BOOLEAN bSetMac = FALSE;
	static struct file * hFsFile = NULL;

	//Added by Shen Wei 2002.5.10
	MemoryReadContext * pReadContext;
	
	static BOOLEAN bMoreCmd = FALSE;
	static BOOLEAN bUpLoaded = FALSE;
	
	
	static unsigned char iIpSequenceNo = 0xff;
	
	
	CM_PACKET * pCmd = NULL;
	int iCount=0;

	//End
	
	bMoreCmd = FALSE;
	
	if(skb_queue_empty(&phsb_device->cmd_recv_queue_head))
		return 0;
//		spin_lock_irqsave(&phsb_device->cmdrecvqlock, iflags);
//		skb_queue_tail(&phsb_device->cmd_recv_queue_head,skb);
//		spin_unlock_irqrestore(&phsb_device->cmdrecvqlock, iflags);

	spin_lock_irqsave(&phsb_device->cmdrecvqlock, iflags);
	while((skb=skb_dequeue(&phsb_device->cmd_recv_queue_head))!=NULL)
	{
		spin_unlock_irqrestore(&phsb_device->cmdrecvqlock, iflags);
		DBG_PRINT00( "CmdRcvThread: skb->len=%d\n", skb->len );
		
		if(signal_pending(current))
		{
					
//			CmCommandSend(phsb_device, CM_REQUEST_USB_SUSPEND, 0 );
			return -1;
		}		
		if(!hsb_usb_hardware_present)
		{
			kfree_skb ( skb );
			//MALLOCINC();
			MFREEINC();			
			continue;
		}

		iCount = skb->len/sizeof(CM_PACKET);
		pCmd = (CM_PACKET *) skb->data;
//		for( i=0; i<iCount; pCmd ++ )
//		{
		//add the process code here;
		
		DBG_PRINT00("CmdRcvThread: Packet Len = %ld\n", pCmd->Data.CmDownload.dwByteCount );
		DBG_PRINT00("CmdRcvThread: eCommand = %s\n", 
			DBG_CM_REQUEST_CODE(pCmd->uRequest));
		DBG_PRINT00("CmdRcvThread: eCommand = 0x%0x\n",
			pCmd->uRequest);
//	BlDelay(1);
//	CmCommandSend(DeviceObject, 	CM_REQUEST_PACKING_MODE, 0);
		switch (pCmd->uRequest)
		{
		case 0x1:
			break;
		case CM_REQUEST_CARD_GET_DATA_LINK_STATUS:
			DBG_PRINT00 ( "CmdRcvThread: Link Status = %d, uId = %d Value = %d\n",
				pCmd->uStatus, pCmd->uId, pCmd->Data.Array[0] );
			
			switch ( (END_DEVICE_STATE)pCmd->Data.Array[0] )
			{
			case END_DEVICE_NOT_READY:
				bMoreCmd = FALSE;
				
				break;
			case END_DEVICE_READY:
				
				if ( !phsb_device->bSetMac )
				{
					iIpSequenceNo = 0xff;
					bMoreCmd = TRUE;
					CmCommandSend(phsb_device, 	CM_REQUEST_PACKING_MODE, 1);
							
				}
				else if ((unsigned char)pCmd->Data.Array[1] != iIpSequenceNo)
				{
					if( !exec_cnxtuserapp("hsb0") )
						iIpSequenceNo = (unsigned char)pCmd->Data.Array[1];
					printk(KERN_INFO "ADSL Modem-->IP Sequence Number changed to %d!\n", iIpSequenceNo);
				}
					
			//	BlDelay( 1*1000 );
			//	CmCommandSend(phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );

				break;
			case END_DEVICE_UPLOAD_BLOCK:
				bMoreCmd = TRUE;
				BlDelay ( 1*1000 );
				CmdSendWithUid(phsb_device, CM_REQUEST_GET_LOCATION, LOC_MEM_BLOCK_READ );

				break;
			
			case END_DEVICE_REBOOT_DOWNLOAD:
				DBG_PRINT00 ( "CmdRcvThread: Reboot Download Entering...\n" );
				bMoreCmd = TRUE;
				CmdSendWithUid(phsb_device, CM_REQUEST_GET_LOCATION, LOC_REBOOT_DOWNLOAD );

				break;
				
			case END_DEVICE_SHUT_DOWN:
				DBG_PRINT00 ( "CmdRcvThread: Shut down Entering...\n" );
				break;
				
			case END_DEVICE_ERR_DOWNLOAD:
				if(phsb_device->DevType != DEV_K2)
					break;
				DBG_PRINT00 ( "CmdRcvThread: Err Download Entering...\n" );
				break;
				
			default:
//				CmCommandSend(phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );

				break;
			}
			if( !bMoreCmd )
			{
				BlDelay( 8*1000 );
				if(signal_pending(current))
				{
					
//					CmCommandSend(phsb_device, CM_REQUEST_USB_SUSPEND, 0 );
					return -1;
				}
				CmCommandSend(phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );
			}
			break;
		case CM_REQUEST_PACKING_MODE:
			DBG_PRINT00 ( "CmdRcvThread: packing mode  %d\n",
				pCmd->Data.Array[1] );
			if( pCmd->uStatus )
				bPackingMode = pCmd->Data.Array[1];
			else
				bPackingMode = 0;

			bMoreCmd = TRUE;
		//	if ( phsb_device->bSoftBoot != 1 )
		//		hsb_usb_start ( phsb_device->hsb_eth_dev );
			CmCommandSend(phsb_device, CM_REQUEST_CARD_GET_MAC_ADDRESS, 1 );
//			BlDelay(20*1000);
			break;
			
		case CM_REQUEST_CARD_GET_MAC_ADDRESS:
			DBG_PRINT00 ( "CmdRcvThread: Mac Status = %d\n",
				pCmd->uStatus );
			//memcpy ( phsb_device->hsb_eth_dev->dev_addr, &pCmd->Data.Array[0], 6 );
			//void hsb_eth_set_mac_addr(struct hsb_usb_device* usb_dev, char* mac,int len);
			hsb_eth_set_mac_addr( phsb_device, &pCmd->Data.Array[0], 6);
			phsb_device->bDownload = 1;
			phsb_device->bSetMac = TRUE;

			hsb_usb_start ( phsb_device->hsb_eth_dev );
			netif_start_queue ( phsb_device->hsb_eth_dev );
	
			
			bMoreCmd = FALSE;
			udsl_print_packet ( &pCmd->Data.Array[0], 6 );
			if( phsb_device->DevType == DEV_HASBANI)
				return 1;
			CmCommandSend(phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );

			break;
		
		case CM_REQUEST_GET_LOCATION:
			
			DBG_PRINT00 ( "CmdRcvThread: Loc Status = %d, uId=%d\n",
				pCmd->uStatus, pCmd->uId );
			
			DBG_PRINT00 ( "CmdRcvThread: dwMemStartAddress = 0x%x, dwTotalBytes = %d\n",
				(int)pCmd->Data.CmDownload.dwAddress, (int)pCmd->Data.CmDownload.dwByteCount );
			//BlDelay( 3*1000 );
			//BlDelay( 3*1000 );
			//Added by Shen Wei 2002.05.10
			if( pCmd->uId == LOC_REBOOT_DOWNLOAD )
			{
				
				netif_stop_queue ( phsb_device->hsb_eth_dev );
				hsb_usb_stop(phsb_device->hsb_eth_dev);
				
				phsb_device->bSoftBoot = 0;
				phsb_device->bDownload = 0;
				phsb_device->bSetMac = FALSE;
				
				FwPreLoad( phsb_device );
				
				SendFileSystemToMemory( phsb_device, CONFIG_FILE_NAME );
				
				SendFirmwareToMemory( phsb_device, FIRMWARE_FILE_NAME,
					pCmd->Data.CmDownload.dwAddress, pCmd->Data.CmDownload.dwByteCount );
//				FwPostLoad(phsb_device);
				
BlDelay(1*1000);
				BlGoToAddress( phsb_device, pCmd->Data.CmDownload.dwAddress );
				bMoreCmd = FALSE;
				
				//BlDelay( 1 );
				//CmCommandSend(phsb_device, CM_REQUEST_PACKING_MODE, 1);
				BlDelay( 8*1000 );
				if(signal_pending(current))
					return -1;
				CmCommandSend(phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );

				break;

			}
			
			if( SendFileToBoard == 1 )
			{
				bUpLoaded = TRUE;
				bMoreCmd = FALSE;
				SendFileToBoard = 0;
				CmCommandSend(phsb_device, CM_REQUEST_UPLOAD_BLOCK_COMPLETE, 0 );
				CmCommandSend(phsb_device, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );

				break;

			}
		
			if( !InitReadContext( &pReadContext, pCmd->Data.CmDownload.dwAddress, 
				pCmd->Data.CmDownload.dwByteCount ) )
				break;		
     			if( !phsb_device )
    				break;
    			phsb_device->pMemConexant = pReadContext;
    			
    			hFsFile = OpenConfigFile ( CONFIG_FILE_NAME );
    			if( !hFsFile )
    			{
    				TRACE ("Config File Open Error...\n");
    				free_pages( (unsigned long)pReadContext->pBuffer, 4 );
    				kfree( pReadContext );
    				//MALLOCINC();
				MFREEINC();		
    				phsb_device->pMemConexant = NULL;
     				break;
    			}	
    			WriteToConfigFile( hFsFile, (BYTE *)&pCmd->Data.CmDownload.dwAddress, sizeof(DWORD) );
    			WriteToConfigFile( hFsFile, (BYTE *)&pCmd->Data.CmDownload.dwByteCount, sizeof(DWORD) );
    			bUpLoaded = FALSE;
    			CmdSendReadMem (phsb_device, pReadContext->CurrentAddress, 
    				CM_DOWNLOAD_DATA_MAX*HSB_USB_PACKET_COUNT);
    							
    			break;
    			
		case CM_REQUEST_READ_MEMORY_BLOCK:
			
			if ( bUpLoaded )
				break;
			
			if( SaveMemoryToFile( hFsFile, pReadContext, pCmd ) == 0 )
			{
				bUpLoaded = TRUE;
				bMoreCmd = FALSE;
				hFsFile = NULL;	
			}
			break;
			
		default:
			break;
		}//Switch
		
//		}//For 
		
		kfree_skb ( skb );
		//MALLOCINC();
		MFREEINC();
		
		spin_lock_irqsave(&phsb_device->cmdrecvqlock, iflags);
				
	}//While
	spin_unlock_irqrestore(&phsb_device->cmdrecvqlock, iflags);
	
	return 0;
}

//Return Value: -1 --- File can't open
//		 0 --- File is too small
//		>0 --- Bytes sent

int SendFileSystemToMemory( struct hsb_usb_device* DeviceObject, char * FileName )
{
	int ret = -1;
	DWORD   dwFileBytes = 0;
	DWORD	dwBytes = 0;
	BOOLEAN bLoaded = FALSE;
	DWORD*	pdwFwImage = NULL;
	
	DWORD dwAddress = 0;
	
	DBG_PRINT00 ("SendFileSystemToMemory: Entering...\n");
	bLoaded = FwImageFileLoad( FileName, &pdwFwImage, &dwFileBytes );
	
	if( bLoaded && pdwFwImage )
	{
		ret = 0;
		DBG_PRINT00 ("SendFileSystemToMemory:Begin to send File System...\n");
//		BlDelay( 2*1000 );
		
		if (dwFileBytes > 2*sizeof(DWORD))
		{
			dwAddress = (DWORD) *pdwFwImage;
			dwBytes = (DWORD) *(pdwFwImage+1);
			if( dwBytes == (dwFileBytes - 2*sizeof(DWORD)) )
			{
				BlMemoryWrite(DeviceObject, dwAddress, pdwFwImage+2, dwBytes, MA_DWORD);
				DBG_PRINT00 ("SendFileSystemToMemory: size=%ld  Addr=0x%lx...\n", dwBytes, dwAddress );
				//BlDelay( 3*1000 );
				//BlDelay( 3*1000 );
			}
			else
				FillMemory(DeviceObject, dwAddress, dwBytes, '\0', MA_DWORD );
			ret = dwBytes;
		}		
	}
	if( pdwFwImage )
		MEMORY_VFREE(pdwFwImage);
	
	DBG_PRINT00 ("SendFileSystemToMemory: Leaving...\n");		
	return ret;
}

int SendFirmwareToMemory( struct hsb_usb_device* DeviceObject, char * FileName,
	 DWORD dwAddress, DWORD dwSize )
{
	int ret = -1;
	DWORD   dwBytes = 0;
	BOOLEAN bLoaded = FALSE;
	DWORD*	pdwFwImage = NULL;
	DBG_PRINT00 ("SendFirmwareToMemory: Entering...\n");
	
	bLoaded = FwImageFileLoad( FileName, &pdwFwImage, &dwBytes );
	
	if( bLoaded && ( dwBytes < dwSize ) )
	{
		BlMemoryWrite(DeviceObject, dwAddress, pdwFwImage, dwBytes, MA_DWORD);

					
		ret = dwBytes;
	}
	if( pdwFwImage )
		MEMORY_VFREE(pdwFwImage);
	DBG_PRINT00 ("SendFirmwareToMemory: Leaving...\n");
		
	return ret;
} 

// Return Value: -1 --- Error packet received;
//		  0 --- Finished reading memory
//		  1 --- Continue 

int SaveMemoryToFile( struct file * hFile,
	MemoryReadContext * pCon, CM_PACKET * pCmd  )
{
	pCon = (MemoryReadContext *)adsl_devices[0]->pMemConexant;
	DBG_PRINT00 ( "SaveMemoryToFile: RMEM Status = %d\n",
		pCmd->uStatus );
				
	pCon->Count ++;
	if( pCmd->Data.CmDownload.dwAddress != 	pCon->CurrentAddress )
	{

		DBG_PRINT00 ( "SaveMemoryToFile: Received Packet Error...count=%d\n\t\tdwAddress=%ld CurAddr=%ld\n",
			pCon->Count, pCmd->Data.CmDownload.dwAddress, pCon->CurrentAddress );
		if (pCon->Count == HSB_USB_PACKET_COUNT -1 )
		{
			pCon->Count = 0;
			//BlDelay( 1 );
			CmdSendReadMem (adsl_devices[0], pCon->CurrentAddress, 
				CM_DOWNLOAD_DATA_MAX*HSB_USB_PACKET_COUNT);
		}
		return -1;
	}
	pCon->CurrentAddress += pCmd->Data.CmDownload.dwByteCount;
	pCon->BytesReceived += pCmd->Data.CmDownload.dwByteCount;
			
	if( pCon->Length > BUFFERSIZE - 52 )
	{
		WriteToConfigFile( hFile, pCon->pBuffer,
			pCon->Length );
		pCon->Length = 0;
		MEMZERO( pCon->pBuffer, BUFFERSIZE );
	}
	memcpy( pCon->pBuffer+pCon->Length,
		&pCmd->Data.CmDownload.DownloadData[0], pCmd->Data.CmDownload.dwByteCount );
	pCon->Length += pCmd->Data.CmDownload.dwByteCount;
			
	if( pCon->BytesReceived >= pCon->TotalBytes )
	{
		WriteToConfigFile( hFile, pCon->pBuffer,
			pCon->Length );
		if( hFile )
			CloseConfigFile( hFile );
		pCon->Length = 0;
		pCon->Count = 0;
		free_page( (unsigned long)pCon->pBuffer );
		pCon->pBuffer = NULL;
		if( pCon )
			kfree( pCon );
		adsl_devices[0]->pMemConexant = NULL;
				
//		bUpLoaded = TRUE;
//		bMoreCmd = FALSE;
				
		CmCommandSend(adsl_devices[0], CM_REQUEST_UPLOAD_BLOCK_COMPLETE, 0 );
		BlDelay ( 1 );
		CmCommandSend(adsl_devices[0], CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );
		
		DBG_PRINT00( "SaveMemoryToFile: TotalBytes=%ld, Received=%ld\n", pCon->TotalBytes,
			pCon->BytesReceived );
			
		return 0;
	}
	DBG_PRINT00( "SaveMemoryToFile: TotalBytes=%ld, Received=%ld\n", pCon->TotalBytes,
		pCon->BytesReceived );		
	if( pCon->Count == HSB_USB_PACKET_COUNT - 1 )
	{
		pCon->Count = 0;
		//BlDelay( 1 );
		if( (pCon->TotalBytes-pCon->BytesReceived) >= 
				CM_DOWNLOAD_DATA_MAX*HSB_USB_PACKET_COUNT )	
			CmdSendReadMem (adsl_devices[0], pCon->CurrentAddress,
				CM_DOWNLOAD_DATA_MAX*HSB_USB_PACKET_COUNT);
		else
			CmdSendReadMem (adsl_devices[0], pCon->CurrentAddress, 
				(pCon->TotalBytes-pCon->BytesReceived) );
					
	}
	
	return 1;
}

BOOLEAN 
CmdSendWithUid
(
	IN PDEVICE_OBJECT	DeviceObject,
	IN	CM_REQUEST_CODE	eCommand, 
	IN	ADDRESS_TYPE	uId
)
{
	BOOLEAN bResult = FALSE;
	CM_PACKET * CmReq;
	
	if ( (CmReq = kmalloc(sizeof(CM_PACKET), GFP_KERNEL)) == NULL )
	{
	        return FALSE;
	}
	MEMSET (CmReq, 0, sizeof(CM_PACKET));
	CmReq->uRequest = eCommand;
	CmReq->uId = uId;
	
	bResult = CmPacketSend(DeviceObject, CmReq);
	
	if( CmReq )	kfree( CmReq );
	
	return bResult;	
	
}

BOOLEAN
CmdSendReadMem
(
	IN PDEVICE_OBJECT	DeviceObject,
	IN DWORD		dwAddress,
	IN DWORD		dwCount
)
{
	BOOLEAN bResult = FALSE;
	CM_PACKET * CmReq;
	
	if ( (CmReq = kmalloc(sizeof(CM_PACKET), GFP_KERNEL)) == NULL )
	{
	        return FALSE;
	}
	MEMSET (CmReq, 0, sizeof(CM_PACKET));
	CmReq->uRequest = CM_REQUEST_READ_MEMORY_BLOCK;
	CmReq->Data.CmDownload.dwByteCount = dwCount;
	CmReq->Data.CmDownload.dwAddress = dwAddress;
	
	bResult = CmPacketSend(DeviceObject, CmReq);
	
	if( CmReq )	kfree( CmReq );
	
	return bResult;		
}

struct file * OpenConfigFile(char * FileName)//BYTE * pBuf, DWORD dwSize)
{
	struct file * hFile;
	TRACE( "OpenConfigFile. Entering... FileName=%s\n\n", FileName );
	if( !FileName )
		return NULL;
		
	 /* Save uid and gid used for filesystem access.
	  * Set user and group to 0 (root)
 	  */
	orgfsuid=current->fsuid;
	orgfsgid=current->fsgid;
	current->fsuid=current->fsgid=0;
	/* save FS register and set FS register to kernel
	 * space, needed for read and write to accept
	 * buffer in kernel space.
	 */
 	orgfs=get_fs();
// 	set_fs(get_ds());
	set_fs(KERNEL_DS);	

//	if ((hFsFile = FsFileCreate(pwszFileName)) == NULL)
	hFile = filp_open(FileName, O_WRONLY|O_CREAT|O_TRUNC, 0644);
	//get_file(hFile);	
//	atomic_inc(&(hFile)->f_count);
	TRACE("OpenConfigFile. Leaving... %p file->count=%d\n\n", hFile, hFile->f_count.counter );
//	BlDelay(5*1000);
	return hFile;
}

BOOLEAN WriteToConfigFile( struct file *hFile, BYTE * pBuf, ULONG len )
{
	BOOLEAN bRet = FALSE;
	TRACE( "WriteToConfigFile. Entering... len=%ld\n\n", len );
	if( hFile == NULL || len <= 0 )
	{
		TRACE( "WriteToConfigFile: hFile=%p,len=%ld\n", hFile, len );
		return FALSE;
	}
 	if (hFile&&hFile->f_op&&hFile->f_op->write)
 	{
 
        	if( len != (hFile->f_op->write(hFile, pBuf, len, &hFile->f_pos)))
            	{
            		TRACE("File write error len = %lx\n", len);
            		bRet = FALSE;
	       	}else
	       		bRet = TRUE;
        }
        TRACE( "WriteToConfigFile. Leaving... bRet=%d length=%ld\n", bRet, len );
        return bRet;	
}

BOOLEAN CloseConfigFile(struct file *hFile)
{
	
	#ifdef DEBUG1
	int i=0;
	#endif
	
	TRACE("CloseConfigFile. Entering... \n\n");	
	if (hFile)
	{

		if(hFile->f_op->flush)
			hFile->f_op->flush(hFile);

		filp_close(hFile, NULL);
//		i = hFile->f_count.counter;
		
	 	TRACE("filp_close return i=%d\n", i );	
//	 	BlDelay( 5*1000 );
	}
	
 	set_fs(orgfs);
  	current->fsuid=orgfsuid;
 	current->fsgid=orgfsgid;

 	TRACE("CloseConfigFile. Leaving... \n\n");	
 	return TRUE;
}

BOOLEAN InitReadContext( MemoryReadContext ** pCon, DWORD dwAddr, DWORD dwBytes )
{
	TRACE( "InitReadContext: Entering...\n" );
	*pCon = kmalloc( sizeof(MemoryReadContext), GFP_KERNEL );
	if( NULL == (*pCon) )
	{
		TRACE( "InitReadContext: (*pCon) No memory...\n" );
		return FALSE;
	}
	MALLOCINC();
	//MFREEINC();
	(*pCon)->CurrentAddress = dwAddr;
	(*pCon)->TotalBytes = dwBytes;
	(*pCon)->BytesReceived = 0;
	(*pCon)->Count = 0;
	(*pCon)->Length = 0;
//    			(*pCon)->pBuffer = __get_free_page(GFP_KERNEL);;
	(*pCon)->pBuffer =  (BYTE *)__get_free_pages(GFP_KERNEL, 4);
        if (NULL == (*pCon)->pBuffer)
	{
		TRACE ("InitReadContext:(*pCon)->Buffer No memory...\n");
		if ( (*pCon) )
			kfree( (*pCon) );
		
		return FALSE;
	}
	MEMZERO( (*pCon)->pBuffer, BUFFERSIZE );
	
	TRACE( "InitReadContext: Leaving...\n" );
	return TRUE;
	
}

NTSTATUS FillMemory( IN PDEVICE_OBJECT	DeviceObject,
    DWORD dwAddress, DWORD uSize, UCHAR   value,
    BL_MEMORY_ACCESS uAccessType)
{
    // allocate a buffer
    NTSTATUS status = STATUS_SUCCESS;
    

    size_t const BufLen = 0x1000;
    UCHAR * buf = NULL;
    buf = (UCHAR *)__get_free_page(GFP_KERNEL);
    
    if (NULL == buf)
    {
        return STATUS_NO_MEMORY;
    }
    memset(buf, value, BufLen);
    // send 
    while (uSize)
    {
        ULONG ToSend = uSize;
        if (ToSend > BufLen)
        {
            ToSend = BufLen;
        }

        status = BlMemoryWrite(DeviceObject, dwAddress, buf, ToSend, uAccessType);
        if ( ! NT_SUCCESS(status))
        {
            break;
        }

        uSize -= ToSend;
        dwAddress += ToSend;
    }

    if( buf )
    	free_page( (unsigned long)buf );
    	
    return status;
}	




