/*
 * Copyright (c) 2010, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
*   @file  timm_osal_pipes.c
*   This file contains methods that provides the functionality
*   for creating/using Nucleus pipes.
*
*  @path \
*
*/
/* -------------------------------------------------------------------------- */
/* =========================================================================
 *!
 *! Revision History
 *! ===================================
 *! 07-Nov-2008 Maiya ShreeHarsha: Linux specific changes
 *! 0.1: Created the first draft version, ksrini@ti.com
 * ========================================================================= */

/******************************************************************************
* Includes
******************************************************************************/

#include "timm_osal_types.h"
#include "timm_osal_error.h"
#include "timm_osal_memory.h"
#include "timm_osal_trace.h"

#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

/**
* TIMM_OSAL_PIPE structure define the OSAL pipe
*/
typedef struct TIMM_OSAL_PIPE
{
	int pfd[2];
	TIMM_OSAL_U32 pipeSize;
	TIMM_OSAL_U32 messageSize;
	TIMM_OSAL_U8 isFixedMessage;
	int messageCount;
	int totalBytesInPipe;
} TIMM_OSAL_PIPE;


/******************************************************************************
* Function Prototypes
******************************************************************************/

/* ========================================================================== */
/**
* @fn TIMM_OSAL_CreatePipe function
*
*
*/
/* ========================================================================== */

TIMM_OSAL_ERRORTYPE TIMM_OSAL_CreatePipe(TIMM_OSAL_PTR * pPipe,
    TIMM_OSAL_U32 pipeSize,
    TIMM_OSAL_U32 messageSize, TIMM_OSAL_U8 isFixedMessage)
{
	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
	TIMM_OSAL_PIPE *pHandle = TIMM_OSAL_NULL;

	pHandle =
	    (TIMM_OSAL_PIPE *) TIMM_OSAL_Malloc(sizeof(TIMM_OSAL_PIPE), 0, 0,
	    0);

	if (TIMM_OSAL_NULL == pHandle)
	{
		bReturnStatus = TIMM_OSAL_ERR_ALLOC;
		goto EXIT;
	}
	TIMM_OSAL_Memset(pHandle, 0x0, sizeof(TIMM_OSAL_PIPE));

	pHandle->pfd[0] = -1;
	pHandle->pfd[1] = -1;
	if (SUCCESS != pipe(pHandle->pfd))
	{
		TIMM_OSAL_Error("Pipe failed: %s!!!", strerror(errno));
		goto EXIT;
	}

	pHandle->pipeSize = pipeSize;
	pHandle->messageSize = messageSize;
	pHandle->isFixedMessage = isFixedMessage;
	pHandle->messageCount = 0;
	pHandle->totalBytesInPipe = 0;

	*pPipe = (TIMM_OSAL_PTR) pHandle;

	bReturnStatus = TIMM_OSAL_ERR_NONE;


	return bReturnStatus;
EXIT:
	TIMM_OSAL_Free(pHandle);
	return bReturnStatus;
}



/* ========================================================================== */
/**
* @fn TIMM_OSAL_DeletePipe function
*
*
*/
/* ========================================================================== */

TIMM_OSAL_ERRORTYPE TIMM_OSAL_DeletePipe(TIMM_OSAL_PTR pPipe)
{
	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_NONE;

	TIMM_OSAL_PIPE *pHandle = (TIMM_OSAL_PIPE *) pPipe;

	if (TIMM_OSAL_NULL == pHandle)
	{
		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
		goto EXIT;
	}

	if (SUCCESS != close(pHandle->pfd[0]))
	{
		TIMM_OSAL_Error("Delete_Pipe Read fd failed!!!");
		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
	}
	if (SUCCESS != close(pHandle->pfd[1]))
	{
		TIMM_OSAL_Error("Delete_Pipe Write fd failed!!!");
		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
	}

	TIMM_OSAL_Free(pHandle);
EXIT:
	return bReturnStatus;
}



/* ========================================================================== */
/**
* @fn TIMM_OSAL_WriteToPipe function
*
*
*/
/* ========================================================================== */

TIMM_OSAL_ERRORTYPE TIMM_OSAL_WriteToPipe(TIMM_OSAL_PTR pPipe,
    void *pMessage, TIMM_OSAL_U32 size, TIMM_OSAL_S32 timeout)
{
	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
	TIMM_OSAL_U32 lSizeWritten = -1;

	TIMM_OSAL_PIPE *pHandle = (TIMM_OSAL_PIPE *) pPipe;

	if (size == 0)
	{
		TIMM_OSAL_Error("0 size!!!");
		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
		goto EXIT;
	}
	lSizeWritten = write(pHandle->pfd[1], pMessage, size);

	if (lSizeWritten != size)
	{
		TIMM_OSAL_Error("Write of pipe failed!!!");
		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
		goto EXIT;
	}

	/*Update message count and size */
	pHandle->messageCount++;
	pHandle->totalBytesInPipe += size;

	bReturnStatus = TIMM_OSAL_ERR_NONE;

      EXIT:
	return bReturnStatus;
}



/* ========================================================================== */
/**
* @fn TIMM_OSAL_WriteToFrontOfPipe function
*
*
*/
/* ========================================================================== */

TIMM_OSAL_ERRORTYPE TIMM_OSAL_WriteToFrontOfPipe(TIMM_OSAL_PTR pPipe,
    void *pMessage, TIMM_OSAL_U32 size, TIMM_OSAL_S32 timeout)
{

	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
	TIMM_OSAL_U32 lSizeWritten = -1;
	TIMM_OSAL_U32 lSizeRead = -1;
	TIMM_OSAL_PIPE *pHandle = (TIMM_OSAL_PIPE *) pPipe;
	TIMM_OSAL_U8 *tempPtr = NULL;


	/*First write to this pipe */
	if (size == 0)
	{
		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
		goto EXIT;
	}

	lSizeWritten = write(pHandle->pfd[1], pMessage, size);

	if (lSizeWritten != size)
	{
		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
		goto EXIT;
	}

	/*Update number of messages */
	pHandle->messageCount++;


	if (pHandle->messageCount > 1)
	{
		/*First allocate memory */
		tempPtr =
		    (TIMM_OSAL_U8 *) TIMM_OSAL_Malloc(pHandle->
		    totalBytesInPipe, 0, 0, 0);

		if (tempPtr == NULL)
		{
			bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
			goto EXIT;
		}

		/*Read out of pipe */
		lSizeRead =
		    read(pHandle->pfd[0], tempPtr, pHandle->totalBytesInPipe);

		/*Write back to pipe */
		lSizeWritten =
		    write(pHandle->pfd[1], tempPtr,
		    pHandle->totalBytesInPipe);

		if (lSizeWritten != size)
		{
			bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
			goto EXIT;
		}

		/*Update Total bytes in pipe */
		pHandle->totalBytesInPipe += size;
	}


      EXIT:
	TIMM_OSAL_Free(tempPtr);

	return bReturnStatus;

}



/* ========================================================================== */
/**
* @fn TIMM_OSAL_ReadFromPipe function
*
*
*/
/* ========================================================================== */

TIMM_OSAL_ERRORTYPE TIMM_OSAL_ReadFromPipe(TIMM_OSAL_PTR pPipe,
    void *pMessage,
    TIMM_OSAL_U32 size, TIMM_OSAL_U32 * actualSize, TIMM_OSAL_S32 timeout)
{
	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
	TIMM_OSAL_U32 lSizeRead = -1;
	TIMM_OSAL_PIPE *pHandle = (TIMM_OSAL_PIPE *) pPipe;

	if (size == 0)
	{
		TIMM_OSAL_Error("nRead size has error!!!");
		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
		goto EXIT;
	}
	if ((pHandle->messageCount == 0) && (timeout == TIMM_OSAL_NO_SUSPEND))
	{
		/*If timeout is 0 and pipe is empty, return error */
		TIMM_OSAL_Error("Pipe is empty!!!");
		bReturnStatus = TIMM_OSAL_ERR_PIPE_EMPTY;
		goto EXIT;
	}
	if ((timeout !=TIMM_OSAL_NO_SUSPEND) &&
	    (timeout != (TIMM_OSAL_S32)TIMM_OSAL_SUSPEND))
	{
		TIMM_OSAL_Warning("Only infinite or no timeouts \
			supported. Going to read with infinite timeout now");
	}
	/*read blocks infinitely until message is available */
	*actualSize = lSizeRead = read(pHandle->pfd[0], pMessage, size);
	if (0 == lSizeRead)
	{
		TIMM_OSAL_Error("EOF reached or no data in pipe!!!");
		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
		goto EXIT;
	}

	bReturnStatus = TIMM_OSAL_ERR_NONE;

	pHandle->messageCount--;
	pHandle->totalBytesInPipe -= size;

      EXIT:
	return bReturnStatus;

}



/* ========================================================================== */
/**
* @fn TIMM_OSAL_ClearPipe function
*
*
*/
/* ========================================================================== */

TIMM_OSAL_ERRORTYPE TIMM_OSAL_ClearPipe(TIMM_OSAL_PTR pPipe)
{
	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR;

	TIMM_OSAL_Warning("This function is currently not implemented");

	return bReturnStatus;
}



/* ========================================================================== */
/**
* @fn TIMM_OSAL_IsPipeReady function
*
*
*/
/* ========================================================================== */

TIMM_OSAL_ERRORTYPE TIMM_OSAL_IsPipeReady(TIMM_OSAL_PTR pPipe)
{
	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR;
	TIMM_OSAL_PIPE *pHandle = (TIMM_OSAL_PIPE *) pPipe;

	if (pHandle->messageCount <= 0)
	{
		bReturnStatus = TIMM_OSAL_ERR_NOT_READY;
	} else
	{
		bReturnStatus = TIMM_OSAL_ERR_NONE;
	}

	return bReturnStatus;

}



/* ========================================================================== */
/**
* @fn TIMM_OSAL_GetPipeReadyMessageCount function
*
*
*/
/* ========================================================================== */

TIMM_OSAL_ERRORTYPE TIMM_OSAL_GetPipeReadyMessageCount(TIMM_OSAL_PTR pPipe,
    TIMM_OSAL_U32 * count)
{
	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_NONE;
	TIMM_OSAL_PIPE *pHandle = (TIMM_OSAL_PIPE *) pPipe;

	*count = pHandle->messageCount;
	return bReturnStatus;

}