/*********************************************************************************

Copyright(c) 2007 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 for Consolee I/O.
				
				Please note, to use this module, you also need to include a copy
				of primio.lib in your project that defines the stdin, stdout, and
				stderr descriptors as dev_not_claimed.
				
*********************************************************************************/

#include <services/services.h>      /* system services                     */
#include <services/fss/adi_fss.h>	/* File System Service 				   */
#include "consoleio.h"

#include <stdio.h>
#include <string.h>
#include <device.h>
#include <device_int.h>

#define UARTIO 4
#define READ 1
#define WRITE 0

/* Entry point functions */
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);

/* Static functions */
static u32 WriteTextStream( ADI_DEV_DEVICE_HANDLE DeviceHandle, u8 *buf, s32 size );
static u32 ProcessTextStream(u8 *buf, u32 size);
static u32 Transfer(ADI_DEV_DEVICE_HANDLE DeviceHandle, u8 *Data, u32 Size, u32 ReadFlag );

/* SSL and Device Manager initialization data */
extern ADI_FSS_DEVICE_DEF ADI_UART_ConsoleIO_Def;

/* DevEntry structure is exported to application to set Device ID and config data
*/
DevEntry adi_console_entry = {
	UARTIO,				            /* Device ID for libIO purposes */
	NULL,				            /* Data area */
	adi_console_init,				/* init function point */
	adi_console_open,				/* open function pointer */	
	adi_console_close,				/* close function pointer */
	adi_console_write,				/* write function pointer */
	adi_console_read,				/* read function pointer */
	adi_console_seek,				/* seek function pointer */
	(int)&ADI_UART_ConsoleIO_Def,	/* stdinfd */
	(int)&ADI_UART_ConsoleIO_Def,	/* stdoutfd */
	(int)&ADI_UART_ConsoleIO_Def	/* stderrfd */
};



/* The Deferred Callback, DMA and Device Manager Handles are defined elsewhere
*/
extern ADI_DCB_HANDLE         adi_dcb_QueueHandle;     /*  handle to DCB Queue          */
extern ADI_DMA_MANAGER_HANDLE adi_dma_ManagerHandle;   /*  handle to the DMA manager    */
extern ADI_DEV_MANAGER_HANDLE adi_dev_ManagerHandle;   /*  handle to the device manager */


/*********************************************************************
*	Static Functions & Data
*********************************************************************/
static VDK_ThreadID *pConsoleIOThread;

static ADI_SEM_HANDLE DeviceSemaphoreHandle;
static VDK_MessageID msgID_XFER;
static ADI_CONSOLEIO_XFER_MSG xfer_msg;

/*********************************************************************
*	Function:		init
*	Description:	Assigns/Initializes the Device Manager
*********************************************************************/
static int   
adi_console_init(struct DevEntry *pDevEntry)
{
	u32 i, result, ResponseCount;
	int DeviceID;


	// table of device descriptors
	int *fdesc = (int*)&pDevEntry->stdinfd;
	// and boolean table to determine how many times adi_dev_Open is to be called
	int opentab[3] = {0.0,0};
		
	if ( pDevEntry->stdinfd!=dev_not_claimed) 
		opentab[0] = 1;

	if ( pDevEntry->stdoutfd!=dev_not_claimed && pDevEntry->stdoutfd!=pDevEntry->stdinfd ) 
		opentab[1] = 1;
	
	if (pDevEntry->stderrfd!=dev_not_claimed && pDevEntry->stderrfd!=pDevEntry->stdinfd && pDevEntry->stderrfd!=pDevEntry->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] ) {
			pConsoleIOThread = (VDK_ThreadID *)pDevEntry->data;
		}
	}
	
	/* Create Data completion semaphore */
	adi_sem_Create( 0, &DeviceSemaphoreHandle, NULL );
	
	// Turn buffering off for console I/O, 
	if ( pDevEntry->stdinfd!=dev_not_claimed)
		setvbuf(stdin,NULL,_IONBF,0);
	if ( pDevEntry->stdoutfd!=dev_not_claimed)
		setvbuf(stdout,NULL,_IONBF,0);
	if ( pDevEntry->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
*********************************************************************/
static int   
adi_console_open(const char *name, int mode)
{
	return -1;
}

/*********************************************************************
*	Function:		close
*	Description:	Closes the file, and frees memory
*********************************************************************/
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
*********************************************************************/
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
							);
	return szWritten;
}

/*********************************************************************
*	Function:		read
*	Description:	reads into given buffer from current file position
*********************************************************************/
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);
		
	/* replace CRLF with LF	*/
	szRead = ProcessTextStream(buf, szRead);
	
	return szRead;
}

/*********************************************************************
*	Function:		seek
*	Description:	seeks to new position according to arguments
*********************************************************************/
static long  
adi_console_seek(int fd, long offset, int whence)
{
	return -1;
}

#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.
*********************************************************************/
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.
*********************************************************************/
static u32 WriteTextStream( ADI_DEV_DEVICE_HANDLE DeviceHandle, u8 *buf, s32 size )
{
    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 );

            // 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 );
        if( !startpos || startpos!=(size-1) )
            szWritten2 = szWritten;
    }

    return size;
}

/*********************************************************************
    Function:       Transfer
    Description:    Sends the request as a ADI_CONSOLEIO_MSG_XFER_REQUEST
                    structure to the Console I/O thread.
*********************************************************************/
static u32 Transfer(ADI_DEV_DEVICE_HANDLE DeviceHandle, u8 *Data, u32 Size, u32 ReadFlag )
{
    u32 i, Result = ADI_FSS_RESULT_SUCCESS;
    
    /* Create Message to send to Console I/O Thread */
	VDK_MessageID status;

	xfer_msg.ReadFlag = ReadFlag;
	
    xfer_msg.Buffer.Data               = (void*)Data;
    xfer_msg.Buffer.ElementCount       = Size;
    xfer_msg.Buffer.ElementWidth       = sizeof(u8);
    xfer_msg.Buffer.CallbackParameter  = &xfer_msg;
    xfer_msg.Buffer.pAdditionalInfo    = NULL;
    xfer_msg.Buffer.ProcessedFlag      = FALSE;
    xfer_msg.Buffer.pNext              = NULL;
    
    xfer_msg.SemaphoreHandle = DeviceSemaphoreHandle;
	
	/* Create data transfer message */
	msgID_XFER = VDK_CreateMessage( ADI_CONSOLEIO_MSG_XFER_REQUEST, sizeof(ADI_CONSOLEIO_XFER_MSG), (void*)&xfer_msg );
    
	/* Send Message */
	VDK_PostMessage( *pConsoleIOThread, msgID_XFER, ADI_CONSOLEIO_CHANNEL );
	
	//msgID_XFER = VDK_PendMessage(ADI_CONSOLEIO_CHANNEL, (ADI_CONSOLEIO_MSG_TIMEOUT|VDK_kNoTimeoutError));
	
    /* pend on data completion semaphore */	
	adi_sem_Pend (DeviceSemaphoreHandle, ADI_SEM_TIMEOUT_FOREVER );

   	return Result;
}

/*********************************************************************
*********************************************************************/
