/****************************************************************************
*
*	Name:			BlSrv.c
*
*	Description:	Bootloader Service routines implementation.
*
*	Copyright:		(c) 1999 Conexant Systems Inc.
*
*	Revision:	Date:		Author:				Description:
*
*	00.00.0000	01/01/00	George G. Vlasov	Created.
*	
*	Linux-1.0	May 29 2002	Shen Wei Wang			Modified.
*					shenwei.wang@conexant.com
*
*****************************************************************************
*	$Author:   Shen Wei Wang  $
*	$Revision:   1.0  $
*	$Modtime:   May 29 2002 10:30:48  $
****************************************************************************/


//#define DRIVER
/*
#include "USBADSL.h"

#include "Ndisif.h"
*/
#include <linux/usb.h>
//#include "ntdef.h"
#include "BlDefs.h"
#include "hasbani.h"
#include "FwInit.h"
#include "usbsndcm.h"
//#include "usb-uhci.h"
#include <asm/io.h>
/****************************************************************************
*  Helper definitions
****************************************************************************/

/****************************************************************************
*  Constant definitions
****************************************************************************/

#define USB_DATAGRAM_SIZE_MAX	(4 * 1024)

/****************************************************************************
*  Type definitions
****************************************************************************/
/*
typedef enum
{
	USB_CMD_ERR,
	USB_GET_VER,
	USB_READ_MEM,
	USB_WRITE_MEM,
	USB_RMW_MEM,
	USB_CHECKSUM_MEM,
	USB_GOTO_MEM
} BL_COMMAND;

typedef struct tagUSBCMDSTRUCT 
{
	BYTE  USBCmd;
	BYTE  ByteCount;
	BYTE  AccessType;
	BYTE  AckRequest;
	DWORD dwAddress;
	BYTE  USBData[56];
} USBCMDSTRUCT, *PUSBCMDSTRUCT;  
*/
//typedef usb_complete_t MINIPORTCOMPLETIONROUTINE;



/****************************************************************************
*  Function prototypes
****************************************************************************/
extern int hsb_usb_cmd_init(struct hsb_usb_device* phsb_device);
extern int SendFileSystemToMemory( struct hsb_usb_device* DeviceObject, char * FileName );
extern int SendFileToBoard;
/****************************************************************************
*  Object definitions
****************************************************************************/

/****************************************************************************
*  Function implementations
****************************************************************************/

/****************************************************************************
*
*	Name:			BlDelay
*
*	Description:	Delays execution.
*
*	Inputs:			
*
*	Outputs:		Returns TRUE if successful, otherwise FALSE.
*
****************************************************************************/

BOOLEAN
BlDelay
(
	IN	ULONG	uInterval	/* milliseconds */ 
)
{
	/*
	KTIMER		Timer;
	NTSTATUS	ntStatus;
	BOOLEAN		bSuccess	= TRUE;

	//DBG_PRINT00(("BlDelay: ++{"));

	KeInitializeTimerEx(&Timer, SynchronizationTimer);

	KeSetTimer
	(
		&Timer, 
		RtlExtendedIntegerMultiply
		(
			RtlConvertUlongToLargeInteger(uInterval), 
			-1L * 1000 * 1000 / 100
		), 
		NULL
	);

	ntStatus = KeWaitForSingleObject(
		&Timer, Executive, KernelMode, FALSE, NULL);

	bSuccess = NT_SUCCESS(ntStatus);

	//DBG_PRINT00(("BlDelay: }--"));
*/
	PDEBUG("\nBegin to wait for %ds....\n", (int)uInterval / 1000);
//    current->state = TASK_INTERRUPTIBLE;
//    schedule_timeout(uInterval*HZ/1000);

	current->state = TASK_INTERRUPTIBLE;
	schedule_timeout(1 + (uInterval*HZ/1000));
		

	return TRUE;//bSuccess;
}

/****************************************************************************
*
*	Name:			BlTransactionComplete
*
*	Description:	Completes USB transaction.
*
*	Inputs:			
*
*	Outputs:		none.
*
****************************************************************************/

void BlTransactionComplete(urb_t * urb)
{
	PDEVICE_OBJECT instance = (struct hsb_usb_device *)urb->context;
	if (!instance)
		return;
	if ( urb->status < 0 )
		instance->bDldError = 1;	
	wake_up(&instance->iscomplete);
	
	PDEBUG ("BlTransactionComplete: urb->status = %d\n", urb->status);
	
	return;
}


void BlReadTransactionComplete(urb_t * urb)
{
	PDEVICE_OBJECT instance = (struct hsb_usb_device *)urb->context;
	if (!instance)
		return;


	if(urb->status<0)
	    instance->bDldError = 1;
	
	wake_up(&instance->iscomplete);
	
	PDEBUG ("BlReadTransactionComplete: urb->status = %d\n", urb->status);
	
	return;	
}


/****************************************************************************
*
*	Name:			BlDatagramSend
*
*	Description:	Sends USB dtagram.
*
*	Inputs:			
*
*	Outputs:		Returns TRUE if successful, otherwise FALSE.
*
****************************************************************************/

BOOLEAN
BlDatagramSend
(
	IN	PDEVICE_OBJECT	DeviceObject, 
	IN	PVOID			pDatagram, 
	IN	ULONG			uSize
)
{
//   PFDO_DEVICE_EXTENSION    DeviceExtension;
   NTSTATUS				ntStatus;
   BOOLEAN				bSuccess	= TRUE;
	if (!DeviceObject)
		return FALSE;   
	if ( DeviceObject->bDldError )
		return FALSE;
   //DBG_PRINT00(("BlDatagramSend: ++{"));

/*   
   DeviceExtension = DeviceObject->DeviceExtension;
   KeInitializeEvent(
     &DeviceExtension->BLEvent, SynchronizationEvent, FALSE);

   
#if 0   
   //ntStatus = USBADSL_MPSendPacket
   ntStatus = USBADSL_MPSendCommand(
     DeviceObject,
     pDatagram, uSize,
     BlTransactionComplete, DeviceExtension);
#else
   // pipe 7 is an alias of pipe 1, with bulk transfer type
   ntStatus = USBADSL_ProcessMPXFR(
     DeviceObject,
     pDatagram, uSize,
     BlTransactionComplete, DeviceExtension, 7, FALSE);
#endif   


   if (ntStatus == STATUS_PENDING)
   {
     KeWaitForSingleObject(
         &DeviceExtension->BLEvent, Executive, KernelMode, FALSE, NULL);
   }
*/
   ntStatus = USBADSL_ProcessMPXFR(
     DeviceObject,
     pDatagram, uSize,
     BlTransactionComplete, DeviceObject, 1, FALSE);
   
   if ( ntStatus  != -911 )
   	sleep_on_timeout (&DeviceObject->iscomplete, 2*HZ);

   bSuccess = NT_SUCCESS(ntStatus);
/*
   bSuccess = NT_SUCCESS(ntStatus) && 
    NT_SUCCESS(DeviceExtension->BLioStatus.Status) &&
     (DeviceExtension->BLioStatus.Information == uSize);
*/   
   //DBG_PRINT00(("BlDatagramSend: }--"));
   
   return bSuccess;
}



NTSTATUS USBADSL_ProcessMPXFR(
                               IN PDEVICE_OBJECT DeviceObject,
                               IN PCHAR buffer,
                               IN ULONG count,
                               IN MINIPORTCOMPLETIONROUTINE  CallBack,
                               IN PVOID MPContext,
                               IN ULONG pipeNumber,
                               IN BOOLEAN   IOFlag
                               )
{
//	spinlock_t usb_lock = SPIN_LOCK_UNLOCKED;
//	unsigned long flags;
	
    int    err;
    urb_t *urb = 0;
	unsigned int pipe;

	spinlock_t usb_lock = SPIN_LOCK_UNLOCKED;
	unsigned long flags;


    PDEBUG("USBADSL_ProcessMPXFR entered, sending packet %p with length %d\n", buffer, (int)count);
	if (!DeviceObject)
		return -911;
	
	if ( !DeviceObject->dldsndurb )
		return -911;
	if ( DeviceObject->bDldError )
		return -911;
 //   PACKETDEBUG (skb->data, skb->len);

	/* submit packet */
//	urb = usb_alloc_urb(0);
	
	urb = DeviceObject->dldsndurb;
	if(!urb)
	{
		PDEBUG("USBADSL_ProcessMPXFR: usb_alloc_urb failed. \n");
		return -911;
	}
//	DeviceObject->dldsndurb = urb;
	
	if (IOFlag)
		pipe = usb_rcvbulkpipe (DeviceObject->pdev, 0x80 + pipeNumber);
	else
		pipe = usb_sndbulkpipe (DeviceObject->pdev, 0x00 + pipeNumber);

//	spin_lock_irqsave(&usb_lock, flags);	
    
    FILL_BULK_URB(urb,
    DeviceObject->pdev,
	pipe,	//usb_sndbulkpipe(instance->pdev, pipe),
    (unsigned char *)buffer,
     count,
    (usb_complete_t)CallBack,
    MPContext
    );
    urb->transfer_flags|=USB_QUEUE_BULK;
    spin_lock_irqsave(&usb_lock, flags);
   err = usb_submit_urb(urb);
   spin_unlock_irqrestore(&usb_lock, flags);

//   spin_unlock_irqrestore(&usb_lock, flags);
   PDEBUG("USBADSL_ProcessMPXFR (buffer = %p count=%ld)\n", buffer, count);
   PDEBUG("USBADSL_ProcessMPXFR done (retval = %d bFlag=%d)\n", err, IOFlag);
   return err;    
}
/****************************************************************************
*
*	Name:			BlDatagramReceive
*
*	Description:	Receives USB dtagram.
*
*	Inputs:			
*
*	Outputs:		Returns TRUE if successful, otherwise FALSE.
*
****************************************************************************/
NTSTATUS USBADSL_MPReceivePacket (
                                    IN PDEVICE_OBJECT DeviceObject,
                                    IN PCHAR buffer,
                                    IN ULONG count,
                                    IN MINIPORTCOMPLETIONROUTINE ReadCallBack,
                                    IN PVOID hMiniPort
                                 )
{
   NTSTATUS             ntStatus =  STATUS_SUCCESS;
   BOOLEAN		bSuccess = FALSE;

	
   PDEBUG ("enter USBADSL_MPReceivePacket\n");
   if (!DeviceObject)
		return FALSE;

	
   if ( DeviceObject->bDldError )
	return FALSE;
		
   ntStatus = USBADSL_ProcessMPXFR(DeviceObject, buffer, count,
                                  ReadCallBack, hMiniPort, 1, TRUE );
    if(ntStatus != -911)
	sleep_on_timeout (&DeviceObject->iscomplete, 2*HZ);

    bSuccess = NT_SUCCESS(ntStatus);

   PDEBUG ("exit USBADSL_MPReceivePacket\n");
   return bSuccess;
}

BOOLEAN
BlDatagramReceive
(
	IN	PDEVICE_OBJECT	DeviceObject, 
	IN	PVOID			pDatagram, 
	IN	ULONG			uSize
)
{
//   PFDO_DEVICE_EXTENSION    DeviceExtension;
   NTSTATUS				ntStatus;
   BOOLEAN				bSuccess		= TRUE;
   if (!DeviceObject)
	return FALSE;
   
if ( DeviceObject->bDldError )
	return FALSE;
	
   //DBG_PRINT00(("BlDatagramReceive: ++{"));
/*   
   DeviceExtension = DeviceObject->DeviceExtension;
   KeInitializeEvent(
     &DeviceExtension->BLEvent, SynchronizationEvent, FALSE);
*/   
   bSuccess = USBADSL_MPReceivePacket(
     DeviceObject,
     pDatagram, uSize,
     BlReadTransactionComplete, DeviceObject
   );



/*   
   if (ntStatus == STATUS_PENDING)
   {
     KeWaitForSingleObject(
        &DeviceExtension->BLEvent, Executive, KernelMode, FALSE, NULL);
   }
   
   bSuccess = NT_SUCCESS(ntStatus) && 
    NT_SUCCESS(DeviceExtension->BLioStatus.Status) &&
     (DeviceExtension->BLioStatus.Information == uSize);
*/   
   //DBG_PRINT00(("BlDatagramReceive: }--"));
   
   return bSuccess;
}

/****************************************************************************
*
*	Name:			BlMemoryWrite
*
*	Description:	Writes contents of the buffer to the board memory.
*
*	Inputs:			
*
*	Outputs:		Returns TRUE if successful, otherwise FALSE.
*
****************************************************************************/

BOOLEAN
BlMemoryWrite
(
	IN PDEVICE_OBJECT	DeviceObject, 
	DWORD				dwAddress, 
	PVOID				pBuffer, 
	ULONG				uSize, 
	BL_MEMORY_ACCESS				uAccessType
)
{
	PBYTE	pDataBuffer		= pBuffer;
	ULONG	uDataSize		= uSize;
	ULONG	uDataResidual	= uDataSize;

	USBCMDSTRUCT	DatagramPool[1];
	USBCMDSTRUCT*	pDatagramPool		= NULL;
	ULONG			uDatagramSize;
	ULONG			uDatagramResidual	= 0;
	BOOLEAN			bDatagramAllocated	= FALSE;
	USBCMDSTRUCT*	pDatagramBuffer		= NULL;

	BOOLEAN	bSuccess	= TRUE;

	DBG_PRINT00("BlMemoryWrite: ++{\n");

	DBG_PRINT00("BlMemoryWrite: uSize = 0x%08X\n", (int)uSize);
	
	if (!DeviceObject)
		return FALSE;
	if ( DeviceObject->bDldError )
	    return FALSE;
			
	uDatagramSize = MIN(
		USB_DATAGRAM_SIZE_MAX / sizeof(*pDatagramPool), 
		(uDataSize + sizeof(pDatagramPool->USBData) - 1) / 
			sizeof(pDatagramPool->USBData)
	);

/* 
	pDatagramPool = ExAllocatePool(
		NonPagedPool,
		uDatagramSize * sizeof(*pDatagramPool)
	);
*/

	//Modified for Linux 2001.9.6
	pDatagramPool = kmalloc(uDatagramSize * sizeof(*pDatagramPool), GFP_KERNEL);

	if (pDatagramPool == NULL)
	{
		/* Error! */ 

		DBG_PRINT00("BlMemoryWrite: DatagramExAllocatePool Erorr! Using 1 packet buffer.");

		pDatagramPool = DatagramPool;
		uDatagramSize = sizeof(DatagramPool) / sizeof(DatagramPool[0]);
	}
	else
	{
		bDatagramAllocated = TRUE;
	}

	DBG_PRINT00("BlMemoryWrite: uDatagramSize = 0x%08X\n", 
		(int)uDatagramSize * sizeof(*DatagramPool));

	while (uDataResidual > 0)
	{
		USBCMDSTRUCT*	pUsbCommand;
		ULONG			uChunkSize;

		if (uDatagramResidual == 0)
		{
			pDatagramBuffer = pDatagramPool;
			uDatagramResidual = uDatagramSize;
		}

		pUsbCommand = pDatagramBuffer;
		uChunkSize = MIN(uDataResidual, sizeof(pUsbCommand->USBData));

		MEMSET(pUsbCommand, 0, sizeof(*pUsbCommand));

		pUsbCommand->USBCmd = USB_WRITE_MEM;
		pUsbCommand->ByteCount = (BYTE)uChunkSize;
		pUsbCommand->AccessType = uAccessType;
		pUsbCommand->AckRequest = FALSE;
		pUsbCommand->dwAddress = dwAddress;

		MEMCPY(
			pUsbCommand->USBData, 
			pDataBuffer, 
			uChunkSize
		);

		dwAddress += uChunkSize;

		pDataBuffer += uChunkSize;
		uDataResidual -= uChunkSize;

		pDatagramBuffer++;
		uDatagramResidual--;

      		// round byte count, depending on access mode
        	pUsbCommand->ByteCount = (pUsbCommand->ByteCount + ~(-1 << uAccessType)) 
          				  & (-1 << uAccessType);


		if ((uDatagramResidual == 0) || (uDataResidual == 0))
		{
			if (!(bSuccess = BlDatagramSend(
					DeviceObject, 
					pDatagramPool, 
					(uDatagramSize - uDatagramResidual) * sizeof(*pDatagramPool)
				)
			))
			{
				// Error!  

				DBG_PRINT00("BlMemoryWrite: BlDatagramSend Erorr!\n");

				break;
			}
			
		}
	}

	if (bDatagramAllocated)
	{
//		ExFreePool(pDatagramPool);
		kfree(pDatagramPool);
	}

	DBG_PRINT00("BlMemoryWrite: bSuccess = %s\n", (bSuccess ? "TRUE" : "FALSE"));

	DBG_PRINT00("BlMemoryWrite: }--\n");

	return bSuccess;
}

/****************************************************************************
*
*	Name:			BlVerifyChecksum
*
*	Description:	Verifies checksum.
*
*	Inputs:			
*
*	Outputs:		Returns TRUE if successful, otherwise FALSE.
*
****************************************************************************/

BOOLEAN
BlVerifyChecksum
(
	IN PDEVICE_OBJECT	DeviceObject, 
	DWORD				dwChecksum, 
	DWORD				dwAddress, 
	ULONG				uSize
)
{


	BOOLEAN			bSuccess	= TRUE;

	DBG_PRINT00("BlVerifyChecksum: ++{");

	DBG_PRINT00("BlVerifyChecksum: dwChecksum = 0x%08X\n", (int)dwChecksum);
	DBG_PRINT00("BlVerifyChecksum: dwAddress = 0x%08X\n", (int)dwAddress);
	DBG_PRINT00("BlVerifyChecksum: uSize = 0x%08X\n", (int)uSize);
	if (!DeviceObject)
		return FALSE;

	if ( DeviceObject->bDldError )
		return FALSE;
#if 0	
	USBCMDSTRUCT	UsbCommand;
	MEMSET(&UsbCommand, 0, sizeof(UsbCommand));

	UsbCommand.USBCmd = USB_CHECKSUM_MEM;
	UsbCommand.dwAddress = dwAddress;
	*((PDWORD)(UsbCommand.USBData)) = (DWORD)uSize;

	bSuccess = BlDatagramSend(DeviceObject, 
		&UsbCommand, sizeof(UsbCommand));

	if (!bSuccess)
	{
		DBG_PRINT00("BlVerifyChecksum: (BlDatagramSend == FALSE)");
	}
	else
	{
		BlDelay(1*1000);

		MEMSET(&UsbCommand, 0, sizeof(UsbCommand));

		bSuccess = BlDatagramReceive(DeviceObject, 
			&UsbCommand, sizeof(UsbCommand));

		if (!bSuccess)
		{
			DBG_PRINT00("BlVerifyChecksum: (BlDatagramReceive == FALSE)\n");
		}
		else
		{
			DWORD	dwReceivedChecksum	= *(PDWORD)(&UsbCommand.USBData[4]);

			bSuccess = (dwReceivedChecksum == dwChecksum);

			DBG_PRINT00("BlVerifyChecksum: dwReceivedChecksum = 0x%08X\n", (unsigned int )dwReceivedChecksum);
		}
	}
#endif /* 0 */ 

	DBG_PRINT00("BlVerifyChecksum: bSuccess = %s\n", (bSuccess ? "TRUE" : "FALSE"));

	DBG_PRINT00("BlVerifyChecksum: }--\n");

	return bSuccess;
}

/****************************************************************************
*
*	Name:			BlGoToAddress
*
*	Description:	Switches control on the board.
*
*	Inputs:			
*
*	Outputs:		Returns TRUE if successful, otherwise FALSE.
*
****************************************************************************/

BOOLEAN
BlGoToAddress
(
	IN PDEVICE_OBJECT	DeviceObject, 
	DWORD				dwAddress
)
{
	USBCMDSTRUCT	UsbCommand;

	BOOLEAN			bSuccess	= TRUE;

	DBG_PRINT00("BlGoToAddress: ++{\n");
	if (!DeviceObject)
		return FALSE;
	if ( DeviceObject->bDldError )
		return FALSE;

	MEMSET(&UsbCommand, 0, sizeof(UsbCommand));

	UsbCommand.USBCmd = USB_GOTO_MEM;
	UsbCommand.dwAddress = dwAddress;

	bSuccess = BlDatagramSend(DeviceObject, 
		&UsbCommand, sizeof(UsbCommand));

	DBG_PRINT00("BlGoToAddress: bSuccess = %s\n", (bSuccess ? "TRUE" : "FALSE"));

	DBG_PRINT00("BlGoToAddress: }--\n");

	return bSuccess;
}

/****************************************************************************
*
*	Name:			BlCalculateChecksum
*
*	Description:	Calculates checksum.
*
*	Inputs:			
*
*	Outputs:		Returns TRUE if successful, otherwise FALSE.
*
****************************************************************************/

DWORD 
BlCalculateChecksum
(
	PBYTE	pBuffer, 
	ULONG	uSize
)
{
	DWORD	dwChecksum	= 0;
	PDWORD	pdwBuffer	= (PDWORD)pBuffer;
	ULONG	n			= uSize / sizeof(DWORD);
	ULONG	m			= uSize % sizeof(DWORD);

	while (n--) 
	{
		dwChecksum += *pdwBuffer++;
	}

	if (m)
	{
		dwChecksum += *pdwBuffer & (0xFFFFFFFF >> (8 * (sizeof(DWORD) - m)));
	}

	return dwChecksum;
}

/****************************************************************************
*
*	Name:			BlFirmwareInitialize
*
*	Description:	Initializes firmware.
*
*	Inputs:			
*
*	Outputs:		Returns TRUE if successful, otherwise FALSE.
*
****************************************************************************/

BOOLEAN
BlFirmwareInitialize
(
	IN PDEVICE_OBJECT	DeviceObject
)
{
	
	BOOLEAN	bSuccess	= TRUE;

	
	PDEBUG (" BlFirmwareInitialize entered....\n");
	DBG_PRINT00("BlFirmwareInitialize: ++{\n");

	if (!DeviceObject)
		return FALSE;

	bSuccess = FwPreLoad(DeviceObject);

	if ( bSuccess )
		if( SendFileSystemToMemory( DeviceObject, CONFIG_FILE_NAME) > 0 )
			SendFileToBoard = 0;
//	bSuccess = bSuccess&&(FwLoad(DeviceObject)&&FwPostLoad(DeviceObject));
	if ( bSuccess )
		bSuccess = FwLoad(DeviceObject);
	if ( bSuccess )
		bSuccess = FwPostLoad(DeviceObject);
	

//	BlDelay(1);
//	CmCommandSend(DeviceObject, 	CM_REQUEST_PACKING_MODE, 0);
//	BlDelay(1);
//	if ( bSuccess )
//		CmCommandSend(DeviceObject, 	CM_REQUEST_CHIP_ADSL_LINE_START, 0);
//	BlDelay(1);
 	
//	hsb_usb_cmd_init( DeviceObject );
	
//	BlDelay ( 4000); 
	
//	CmCommandSend(DeviceObject, CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 0 );

	
    	DBG_PRINT00("BlFirmwareInitialize: bSuccess = %s\n", (bSuccess ? "TRUE" : "FALSE"));
	
	DBG_PRINT00("BlFirmwareInitialize: }--\n");



	return bSuccess;
}



