/*********************************************************************************

Copyright(c) 2006 Analog Devices, Inc. All Rights Reserved. 

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.  

Description:
				Wrapper module between the CRT library file I/O support
				functions and the UART device driver fro Consoe I/O
				
*********************************************************************************/
#include <mediaplayer.h>
#include <drivers/uart/adi_uart.h>
#include <adi_uart_consoleIO.h>

#include <device.h>
#include <device_int.h>

#define UARTIO 4
#define READ 1
#define WRITE 0
// Prototypes
static int  adi_console_init(struct DevEntry *dev);
static int  adi_console_open(const char *name, int mode);
static int  adi_console_close(int fd);
static int  adi_console_write(int fd, unsigned char *buf, int size);
static int  adi_console_read(int fd, unsigned char *buf, int size);
static long adi_console_seek(int fd, long offset, int whence);

// SSL and Device Manager initialization data
extern ADI_FSS_DEVICE_DEF ADI_UART_ConsoleIO_Def;

static u32 WriteTextStream( ADI_DEV_DEVICE_HANDLE DeviceHandle, u8 *buf, s32 size, ADI_SEM_HANDLE SemaphoreHandle );
static u32 ProcessTextStream(u8 *buf, u32 size);
static u32 Transfer(ADI_DEV_DEVICE_HANDLE DeviceHandle, u8 *Data, u32 Size, u32 ReadFlag, ADI_SEM_HANDLE SemaphoreHandle);

// DevEntry structure is exported to application to set Device ID and config data
DevEntry adi_console_entry = {
	UARTIO,				// Device ID for Device Manager
	NULL,				// Data area
	adi_console_init,				// init function point
	adi_console_open,				// init function pointer	
	adi_console_close,				// init function pointer	
	adi_console_write,				// init function pointer	
	adi_console_read,				// init function pointer	
	adi_console_seek,				// init function pointer
	(int)&ADI_UART_ConsoleIO_Def,	// stdinfd
	(int)&ADI_UART_ConsoleIO_Def,	// stdoutfd
	(int)&ADI_UART_ConsoleIO_Def	// stderrfd
};

static int readCount=0, writeCount=0;

/*********************************************************************
*	Static Function Prototypes
*********************************************************************/
static void Console_Callback(void *fd, u32 Event, void *pArg);


static ADI_SEM_HANDLE ConsoleSemaphoreHandle;

/*********************************************************************
*	FSS non-public Prototypes
*********************************************************************/
extern u32 _adi_fss_WriteTextStream( ADI_DEV_DEVICE_HANDLE DeviceHandle, unsigned char *buf, s32 size, void* pAddInfo, void *pCriticalRegionData);
extern u32 _adi_fss_ProcessTextStream(u8 *buf, u32 size);

/*********************************************************************
*	Function:		init
*	Description:	Assigns/Initializes the Device Manager
*********************************************************************/
section ("sdram0_bank3_app")
static int   
adi_console_init(struct DevEntry *dev)
{
	u32 i, result, ResponseCount;
	int DeviceID;


	// table of device descriptors
	int *fdesc = (int*)&dev->stdinfd;
	// and boolean table to determine how many times adi_dev_Open is to be called
	int opentab[3] = {0.0,0};
		
	if ( dev->stdinfd!=dev_not_claimed) 
		opentab[0] = 1;

	if ( dev->stdoutfd!=dev_not_claimed && dev->stdoutfd!=dev->stdinfd ) 
		opentab[1] = 1;
	
	if (dev->stderrfd!=dev_not_claimed && dev->stderrfd!=dev->stdinfd && dev->stderrfd!=dev->stdoutfd) 
		opentab[2] = 1;
	
	for (i=0;i<2;i++) 
	{
		int fd = fdesc[i];
		ADI_FSS_DEVICE_DEF *pFileDesc = (ADI_FSS_DEVICE_DEF *)(fd);
		
		// Set the direction 
		ADI_DEV_DIRECTION Direction;
		if (i==0 && (fd==fdesc[1] || fd==fdesc[2] ) )
			Direction = ADI_DEV_DIRECTION_BIDIRECTIONAL;
		else if (i==0) 
			Direction = ADI_DEV_DIRECTION_INBOUND;
		else if ( fd!=fdesc[0] ) 
			Direction = ADI_DEV_DIRECTION_OUTBOUND;
			
		if ( opentab[i] ) {
			result = adi_dev_Open(
							adi_dev_ManagerHandle,
							pFileDesc->pEntryPoint,
							pFileDesc->DeviceNumber,
							pFileDesc,
							&pFileDesc->DeviceHandle,
							Direction,
							adi_dma_ManagerHandle,
							adi_dcb_QueueHandle,
							Console_Callback
						);
			// IF ( Failed to open device driver )
			if (result) return -1;
			
			result = adi_dev_Control(
							pFileDesc->DeviceHandle, 
							ADI_DEV_CMD_TABLE, 
							pFileDesc->pConfigTable
							);
			// IF ( Failed to configure device driver )
			if (result) return -1;
			
			/* Enable dataflow once for all time */
			result = adi_dev_Control(pFileDesc->DeviceHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE);

		}
	}
	
	adi_sem_Create( 0, &ConsoleSemaphoreHandle, NULL );
	
	// Turn buffering off for console I/O, 
	if ( dev->stdinfd!=dev_not_claimed)
		setvbuf(stdin,NULL,_IONBF,0);
	if ( dev->stdoutfd!=dev_not_claimed)
		setvbuf(stdout,NULL,_IONBF,0);
	if ( dev->stderrfd!=dev_not_claimed)
		setvbuf(stderr,NULL,_IONBF,0);
	
	return 1;	
}

/*********************************************************************
*	Function:		open
*	Description:	Opens the named file for access according to the 
*					mode argument. Returns File Descriptor Handle
*********************************************************************/
section ("sdram0_bank3_app")
static int   
adi_console_open(const char *name, int mode)
{
	return -1;
}

/*********************************************************************
*	Function:		close
*	Description:	Closes the file, and frees memory
*********************************************************************/
section ("sdram0_bank3_app")
static int   
adi_console_close(int fd)
{
	return -1;
}

#if 0
#define LF   '\n'
#define CR   '\r'
static char crlf[] = "\r\n";
#endif
/*********************************************************************
*	Function:		write
*	Description:	writes given buffer to current file position
*********************************************************************/
section ("sdram0_bank3_app")
static int   
adi_console_write(int fd, unsigned char *buf, int size)
{
	ADI_FSS_DEVICE_DEF *pFileDesc = (ADI_FSS_DEVICE_DEF *)fd;
	u32 szWritten=0 ;

	if (!pFileDesc || !buf) 
		return -1;
		
    szWritten = WriteTextStream(
            					pFileDesc->DeviceHandle, 
								buf,
								size,
								ConsoleSemaphoreHandle
							);
	return szWritten;
}

/*********************************************************************
*	Function:		read
*	Description:	reads into given buffer from current file position
*********************************************************************/
section ("sdram0_bank3_app")
static int   
adi_console_read(int fd, unsigned char *buf, int size)
{
	int szRead;
	ADI_FSS_DEVICE_DEF *pFileDesc = (ADI_FSS_DEVICE_DEF *)fd;
	ADI_DEV_1D_BUFFER ReadBuffer;
	ADI_DEV_1D_BUFFER *pBuffer = &ReadBuffer;
	u32 newpos;

	if (!pFileDesc || !buf) 
		return -1;
		
    szRead = Transfer(pFileDesc->DeviceHandle,buf,(u32)size,READ,ConsoleSemaphoreHandle );
		
	/* replace CRLF with LF	*/
	szRead = ProcessTextStream(buf, szRead);
	
	return szRead;
}

/*********************************************************************
*	Function:		seek
*	Description:	seeks to new position according to arguments
*********************************************************************/
section ("sdram0_bank3_app")
static long  
adi_console_seek(int fd, long offset, int whence)
{
	return -1;
}

/*********************************************************************
*	Function:		Console_Callback
*	Description:	Callback from device driver.
*********************************************************************/
section ("callback_code")
static void Console_Callback(void *fd, u32 Event, void *pArg)
{
	// Cast the hArg pointer to the instance data
	ADI_FSS_SUPER_BUFFER *pSuperBuffer = (ADI_FSS_SUPER_BUFFER *)pArg;
	// CASEOF (event type)
	switch (Event) {
		
		// CASE (buffer processed)
		case ADI_DEV_EVENT_BUFFER_PROCESSED:
		case ADI_DEV_EVENT_SUB_BUFFER_PROCESSED:
			{
				if(pSuperBuffer->Buffer.pAdditionalInfo )
				{
					readCount++;
				}
				else
				{
					writeCount++;	
				}
				/* Post Semaphore */
				adi_sem_Post(ConsoleSemaphoreHandle);
				//adi_sem_Post(pSuperBuffer->SemaphoreHandle);
			}	
			break;
			
		// CASE (an error)
		default: // Raise user exception
			break;		
	// ENDCASE
	}
}

#define LF   '\n'
#define CR   '\r'
static char crlf[] = "\r\n";

/*********************************************************************

    Function:       ProcessTextStream

    Description:    Parses input buffer and replaces CRLF with LF.
                    The buffer is compacted.

*********************************************************************/
section ("sdram0_bank3_app")
static u32 ProcessTextStream(u8 *buf, u32 size)
{
    int i,j, size2=size;
    for (i=0,j=0;i<size;i++,j++)
    {
        buf[j] = buf[i];
        if (i<(size-1) && buf[i]==CR && buf[i+1]==LF)
        {
            // found CRLF sequence
            // replace \r with \n
            buf[j] = LF;
            // shrink buffer
            i++;
            size2--;
        }
    }
    return size2;
}

/*********************************************************************

    Function:       WriteTextStream

    Description:    Processes the input stream, and writes out CRLF
                    in place of each LF.

*********************************************************************/
section ("sdram0_bank3_app")
static u32 WriteTextStream( ADI_DEV_DEVICE_HANDLE DeviceHandle, u8 *buf, s32 size, ADI_SEM_HANDLE SemaphoreHandle )
{
    u32 szWritten=0, szWritten2=0, startpos=0, result;
    u32 i, nbytes=0;

    for (i=0;i<size;i++)
    {
        // increment byte counter for the current buffer section
        nbytes++;
        if ( buf[i]==LF )
        {
            // replace LF with CR
            buf[i] = CR;
            // and write accumulated buffer to file
            szWritten = Transfer(DeviceHandle,(void*)&buf[startpos],nbytes,WRITE,SemaphoreHandle );

            // reinstate LF
            buf[i] = LF;
            // and set startpos to the position of LF to make it the first thing out
            startpos = i;
            szWritten2 += ( szWritten<nbytes ? szWritten : nbytes-1);
            // and reset byte counter to 1 (current LF)
            nbytes = 1;

        }
    }
    u8 LF_at_end = startpos && startpos==(size-1) && i==size;
    u8 Single_LF = szWritten2==0 && size==1 && (*(char*)buf)==LF;
    u8 Trailing_chars = i==size && szWritten2 && startpos<(size-1);

    if ( szWritten2==0 /* None written */ || Trailing_chars || LF_at_end )
    {
        void *pData;
        u32 nbytes;
        if (  Single_LF  )
        {
            pData = (void*)crlf;
            nbytes = 2;
        }
        else if( LF_at_end )
        {
            pData = (void*)&buf[startpos];
            nbytes = 1;
        }
        else if( Trailing_chars )
        {
            pData = (void*)&buf[startpos];
            nbytes = size - startpos;
        }
        else /* no LF in stream */
        {
            pData = (void*)buf;
            nbytes = size;
        }
        szWritten = Transfer(DeviceHandle,pData,nbytes,WRITE,SemaphoreHandle );
        if( !startpos || startpos!=(size-1) )
            szWritten2 = szWritten;
    }

    return size;
}

section ("sdram0_bank3_app")
static u32 Transfer(ADI_DEV_DEVICE_HANDLE DeviceHandle, u8 *Data, u32 Size, u32 ReadFlag, ADI_SEM_HANDLE SemaphoreHandle)
{
    u32 i, Result = ADI_FSS_RESULT_SUCCESS;
    ADI_FSS_SUPER_BUFFER FSSBuffer;

    /* set up Transfer Buffer */
    FSSBuffer.Buffer.Data = (void*)Data;
    FSSBuffer.Buffer.ElementWidth = sizeof(u8);
    FSSBuffer.Buffer.ElementCount = Size;
    FSSBuffer.Buffer.CallbackParameter = &FSSBuffer;
    FSSBuffer.Buffer.pAdditionalInfo = NULL;
    FSSBuffer.Buffer.ProcessedFlag = FALSE;
    FSSBuffer.Buffer.pNext = NULL;
    
    /* Attach completion semaphore */
    FSSBuffer.SemaphoreHandle = SemaphoreHandle;

    /* Queue Buffer */
    if (ReadFlag) {
    	FSSBuffer.Buffer.pAdditionalInfo = (void*)1;
        Result = adi_dev_Read(DeviceHandle,ADI_DEV_1D,(ADI_DEV_BUFFER*)&FSSBuffer);
    }
    else {
    	FSSBuffer.Buffer.pAdditionalInfo = (void*)0;
        Result = adi_dev_Write(DeviceHandle,ADI_DEV_1D,(ADI_DEV_BUFFER*)&FSSBuffer);
    }

    if (Result==ADI_FSS_RESULT_SUCCESS){
        /* Await transfer completion */
        Result = adi_sem_Pend( FSSBuffer.SemaphoreHandle, ADI_SEM_TIMEOUT_FOREVER );
    }
    
    if (readCount)
    printf("%d\n",readCount);

	    if (Result==ADI_FSS_RESULT_SUCCESS){
    	Result = FSSBuffer.Buffer.ProcessedElementCount;
    }
    
   	return Result;
}

