#include <I2CEmulCtxt.h>
#include <oal_i2cemul.h>
#include <stdlib.h>
#include <drvlib_mem.h>



//===== DEBUGGING MACRO DEFINITION =============================================================================
//#define TEST_MODE
#define TEST_IST	FALSE
//==============================================================================================================

//===== MACRRO DEFINITION ======================================================================================
#define IST_TIMEOUT		500 //Unit : ms

#define PRIO				(dwFIFOIdx-1)
#define CURR				(dwFIFOIdx)
#define NEXT				(dwFIFOIdx+1)
#define NEXT_NEXT			(dwFIFOIdx+2)
//==============================================================================================================

//===== GLOBAL VARIABLE DECLARTION =============================================================================
//==============================================================================================================

//===== LOCAL FUNCTION DECLARTION ==============================================================================
VOID CallI2CPollingThread(PI2CEmulContext pEmulContext);
//==============================================================================================================

//===== EXTERN FUNCTION DECLARTION =============================================================================
//==============================================================================================================

I2CEmulContext::I2CEmulContext()
{
	DBGMSG(IIC_FUNC, (L"[I2C:F] +++ I2CEmulContext::I2CHWContext()\r\n"));

	DBGMSG(IIC_FUNC, (L"[I2C:F] --- I2CEmulContext::I2CHWContext()\r\n"));
}

I2CEmulContext::~I2CEmulContext()
{
	DBGMSG(IIC_FUNC, (L"[I2C:F] +++ I2CEmulContext::~I2CHWContext()\r\n"));
	
	DBGMSG(IIC_FUNC, (L"[I2C:F] --- I2CEmulContext::~I2CHWContext()\r\n"));	
}

BOOL
I2CEmulContext::Open(DWORD dwChannel, DWORD dwMethod, DWORD dwThreadPriority, WORD wSlaveAddr, PSXXXXXX_I2C_REG pI2CReg, PSXXXXXX_GPIO_REG pGPIOReg, PSXXXXXX_SYSCLK_REG pSYSCLKReg, PSXXXXXX_BSP_ARG pBSPArgs)
{
#if EMULATION_ENABLE
	DBGMSG(IIC_FUNC, (L"[I2C:F] +++I2CEmulContext::Open()\r\n"));

	bInitialized = TRUE;

	this->pGPIOReg 		= pGPIOReg;	
	this->pBSPArgs		= pBSPArgs;
	this->dwChannel		= dwChannel;
	
	do
	{
		if(I2C_EMUL_Init((BYTE)dwChannel, (PVOID)pGPIOReg, (PVOID)pBSPArgs, 0, 0))
		{
			bInitialized = FALSE;
			break;
		}

		if(!(hI2CEvent = CreateEvent(0,FALSE,FALSE,NULL)))
		{
			bInitialized = FALSE;
			ERRMSG((L"[I2C_EM:E] It's failed to create hI2CEvent\r\n"));
			break;
		}

		if(!(hI2CDoneEvent = CreateEvent(0,FALSE,FALSE,NULL)))
		{
			bInitialized = FALSE;
			ERRMSG((L"[I2C_EM:E] It's failed to create hI2CDoneEvent\r\n"));
			break;
		}

		if ( (hI2CThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CallI2CPollingThread, (LPVOID)this, 0, NULL)) == NULL)
		{
			bInitialized = FALSE;
			ERRMSG((L"[I2C_EM:E] Unable to create IST\r\n"));
		       break;
		}

		dwI2CThreadPriority = dwThreadPriority;
		if(!CeSetThreadPriority(hI2CThread, dwI2CThreadPriority))
		{
			bInitialized = FALSE;
			ERRMSG((L"[I2C_EM:E] Changing IIC Thread Priority is failed\n"));
			break;
		}

	} while(FALSE);

	DBGMSG(IIC_FUNC, (L"[I2C_EM:F] ---I2CEmulContext::Open()\r\n"));
	
	return bInitialized;
#else 
    return TRUE;
#endif
}

BOOL
I2CEmulContext::Close(VOID)
{
	BOOL bClose = TRUE;

	if(!bInitialized) return FALSE;

	do
	{
		if(!TerminateThread(hI2CThread, 0))
		{
			ERRMSG((L"Terminatting I2C Thread is failed\n"));
			bClose = FALSE;
			break;
		}

		if(!CloseHandle(hI2CThread))
		{
			ERRMSG((L"[I2C:E] It's failed to close hI2CThread\r\n"));
			bClose = FALSE;
			break;
		}
	
		if(!CloseHandle(hI2CEvent))
		{
			ERRMSG((L"[I2C:E] It's failed to close hI2CEvent\r\n"));
			bClose = FALSE;
			break;
		}

		if(!CloseHandle(hI2CDoneEvent))
		{
			ERRMSG((L"[I2C:E] It's failed to close hI2CDoneEvent\r\n"));
			bClose = FALSE;
			break;
		}
		
	} while(FALSE);

	return bClose;
}

BOOL			
I2CEmulContext::StartTransfer(PI2CObject pServiceObj)
{
#if EMULATION_ENABLE
	DWORD dwResult;
	DBGMSG(IIC_USR3, (L"[I2C_EM:F] +++ I2CHWContext::StartTransfer(Slave Addr : 0x%X)\r\n", pServiceObj->GetSlaveAddr()));	

	do
	{
		bSuccess = TRUE;
		
		if(!bInitialized)
		{
			ERRMSG((L"[I2C_EM:E] I2C HW CONTEXT IS NOT INITIALIZED\r\n"));
			bSuccess = FALSE;
			break;
		}

		pCurrServObj = pServiceObj;

		if(dwSpeed != CurrServObj.GetClockSpeed())
		{
			dwSpeed = CurrServObj.GetClockSpeed();
			I2C_EMUL_SetSpeed((BYTE)dwChannel, dwSpeed);
		}
		
		dwTxDataCount = 0;
		dwRxDataCount = 0;	
		dwFIFOIdx = 1;

		DBGMSG(IIC_FUNC, (L"[I2C_EM:F] START IST(Channel : %d, Slave : 0x%X)\r\n", dwChannel, CurrServObj.GetSlaveAddr()));
		
		SetEvent(hI2CEvent);
		dwResult = WaitForSingleObject(hI2CDoneEvent, IST_TIMEOUT);
		
		if(dwResult == WAIT_FAILED)
		{
			ERRMSG((L"[I2C_EM:E] WaitForSingleObject : WAIT_FAILED\r\n"));
			LastError = I2CERR_WAIT_FAILED;
			bSuccess = FALSE;		
			if(CloseHandle(hI2CDoneEvent))
    		{
    		    hI2CDoneEvent = CreateEvent(0,FALSE,FALSE,NULL);
    		}	
            else
            {
                ERRMSG((L"[I2C_EM:E] hI2CDoneEvent can't be recreated\r\n"));
			bInitialized = FALSE;
            }
			break;
		}
		else if(dwResult == WAIT_TIMEOUT)
		{
			ERRMSG((L"[I2C_EM:E] WaitForSingleObject : WAIT_TIMEOUT\r\n"));
			LastError = I2CERR_WAIT_TIMEOUT;
			bSuccess = FALSE;
			break;
		}

		DBGMSG(IIC_FUNC, (L"[I2C_EM:F] END IST(Channel : %d, Slave : 0x%X)\r\n", dwChannel, CurrServObj.GetSlaveAddr()));	
	} while(FALSE);
	
	CurrServObj.ClrStateFIFO();		

	pCurrServObj = NULL;

	DBGMSG(IIC_USR3, (L"[I2C_EM:F] --- I2CHWContext::StartTransfer()\r\n"));

	if(bSuccess)
	{
		return TRUE;
	}
	else
	{
	    PowerDown();
		PowerUp();
		return FALSE;
	}
#else
    return TRUE;
#endif
}

VOID
CallI2CPollingThread(PI2CEmulContext pEmulContext)
{
	pEmulContext->I2CThread();
}

VOID			
I2CEmulContext::I2CThread(VOID)
{
#if EMULATION_ENABLE
	BOOL		bExit;
	BYTE		btACK;

	DBGMSG(IIC_FUNC, (L"[IIC_IST:F] +++ I2CEmulThread()\r\n"));

	SetProcPermissions(0xFFFFFFFF);

	do
	{
		__try
		{
			if(WAIT_FAILED == WaitForSingleObject(hI2CEvent, INFINITE))
			{
				ERRMSG((L"[IIC_IST:E] WaitForSingleObject : WAIT_FAILED\r\n"));	
				break;
			}

        	do
        	{
        		bExit = FALSE;
        		switch(CurrServObj[CURR].State)
        		{
        			case I2CSTATE_START:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : START\r\n", dwFIFOIdx));
        				I2C_Start((BYTE)dwChannel);
        				break;
        			case I2CSTATE_SET_ADDR:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : SET_ADDR(0x%x)\r\n", dwFIFOIdx, CurrServObj[CURR].btData));
        				I2C_WriteByte((BYTE)dwChannel, CurrServObj[CURR].btData);
        				break;	
        			case I2CSTATE_SLAVE_DETECTION:	
        				btACK = I2C_GetACK((BYTE)dwChannel);
        				if(btACK == I2C_NAK)
        				{
        					ERRMSG((L"[IIC_IST:E] SLAVE DETECTION IS FAILED\r\n"));
        					bSuccess = FALSE;
        							I2C_Stop((BYTE)dwChannel);
        							bExit = TRUE;
        				}
        				else
        					DBGMSG(TEST_IST, (L"[IIC_IST:I] SLAVE DETECTION IS SUCCESS\r\n"));
        				break;	
        			case I2CSTATE_SET_DATA:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : SET_DATA(0x%x)\r\n", dwFIFOIdx, CurrServObj.pTxBuffer[dwTxDataCount]));
        				I2C_WriteByte((BYTE)dwChannel, CurrServObj.pTxBuffer[dwTxDataCount]);
        				dwTxDataCount++;
        				break;
        			case I2CSTATE_GET_ACK:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : GET_ACK\r\n", dwFIFOIdx));
        				if(*CurrServObj[CURR].pbtData == I2C_ACK)
        				{
        					*CurrServObj[CURR].pbtData = I2C_GetACK((BYTE)dwChannel);
        					if(*CurrServObj[CURR].pbtData == I2C_NAK)
        					{
        						ERRMSG((L"[IIC_IST:E] 0x%x Slave didn't send ACK(%dth data phase), but it's pass\r\n", CurrServObj.GetSlaveAddr(), dwTxDataCount));
        					}
        				}
        				break;
        			case I2CSTATE_GET_DATA:
        				CurrServObj.pRxBuffer[dwRxDataCount] = I2C_ReadByte((BYTE)dwChannel);
        				dwRxDataCount++;
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : GET_DATA(0x%x)\r\n", dwFIFOIdx, CurrServObj.pRxBuffer[dwRxDataCount-1]));
        				break;
        			case I2CSTATE_SET_ACK:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : SET_ACK\r\n", dwFIFOIdx));
        				I2C_SetACK((BYTE)dwChannel);
        				break;
        			case I2CSTATE_SET_NAK:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : SET_NAK\r\n", dwFIFOIdx));
        				I2C_SetNAK((BYTE)dwChannel);
        				break;
        			case I2CSTATE_SET_LOOP:	
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : SET_LOOP\r\n", dwFIFOIdx));
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] BACK INDEX : %d, Time : %d\r\n", (dwFIFOIdx - CurrServObj[CURR].btData), CurrServObj[CURR].dwTime));
        				if(CurrServObj[CURR].dwTime > 0)
        				{
        					CurrServObj[CURR].dwTime--;
        					dwFIFOIdx -= CurrServObj[CURR].btData;
        				}
        				break;
        			case I2CSTATE_STOP:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : STOP\r\n", dwFIFOIdx));
        				I2C_Stop((BYTE)dwChannel);
        				bExit = TRUE;
        				break;
        			case I2CSTATE_WAIT_TO_READY:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : WAIT\r\n", dwFIFOIdx));
        				if(*CurrServObj[CURR].pbtData == TRUE)
        				{
        				    *CurrServObj[CURR].pbtData = I2C_IsSlaveReady();
        				}
        				break;
        			case I2CSTATE_BEGIN:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : BEGIN\r\n", dwFIFOIdx));
        				break;
        			case I2CSTATE_END:
        				DBGMSG(TEST_IST, (L"[IIC_IST:I] STATE(%d) : END\r\n", dwFIFOIdx));
        				break;		
        			default:
        				ERRMSG((L"[IIC_IST:E] UNKNOWN STATE\r\n"));
        				bSuccess = FALSE;
						I2C_Stop((BYTE)dwChannel);
						bExit = TRUE;
        				break;
        		}

        		dwFIFOIdx++;
			} while(!bExit);
		}__except(I2CProcessException(GetExceptionInformation()))
		{
			ERRMSG((L"EXCEPTION IS INVOKED(EMUL %d Channel)\r\n", dwChannel));
			ERRMSG((L"Fifo Index : %d\r\n", dwFIFOIdx));
			ERRMSG((L"Currnet Tx Count : %d\r\n", dwTxDataCount));
			ERRMSG((L"Currnet Rx Count : %d\r\n", dwRxDataCount));
			ERRMSG((L"Current Service Obj : 0x%X\r\n", pCurrServObj));
			if(pCurrServObj)
		    {
				ERRMSG((L"Tx Data Addr & size : 0x%X %d\r\n", pCurrServObj->pTxBuffer, pCurrServObj->dwTxCount));
				ERRMSG((L"Rx Data Addr & size : 0x%X %d\r\n", pCurrServObj->pRxBuffer, pCurrServObj->dwRxCount));
			}
			bSuccess = FALSE;
			I2C_Stop((BYTE)dwChannel);
		}
		
		SetEvent(hI2CDoneEvent);
    }while(TRUE);

	bSuccess = FALSE;	
	bInitialized = FALSE;
	SetEvent(hI2CDoneEvent);
	ERRMSG((L"[IIC_IST:E] I2C IST TERMINATED\r\n"));
	DBGMSG(IIC_FUNC, (L"[IIC_IST:F] --- I2C_IST()\r\n"));
#endif
}
