#include <windows.h>
#include <devload.h>
#include <cregedit.h>
#include <image_cfg.h>

#include <I2CWrapper.h>
#include <I2CHwCtxt.h>

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

#define WINCE_API   TRUE     // WINMO=FALSE

//===== MACRRO DEFINITION ======================================================================================
#define DRIVER_VERSION_INFO 23
#define DEFAULT_THREAD_PRIORITY THREAD_PRIORITY_HIGHEST
#define HWI2C_COUNT 4
#define IS_HWI2C(channel) ((channel < HWI2C_COUNT) ? TRUE : FALSE)

#define EDID_ADDR (0xA0)
//==============================================================================================================

//===== GLOBAL VARIABLE DECLARTION =============================================================================
HANDLE g_hProc;
//==============================================================================================================

//===== LOCAL FUNCTION DECLARTION ==============================================================================
BOOL I2C_PowerUp(PI2C_PUBLIC_CONTEXT pPublicCtxt);
BOOL I2C_PowerDown(PI2C_PUBLIC_CONTEXT pPublicCtxt);
//==============================================================================================================

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

BOOL
DllEntry(
    HINSTANCE   hinstDll,             /*@parm Instance pointer. */
    DWORD   	dwReason,                 /*@parm Reason routine is called. */
    LPVOID  	lpReserved                /*@parm system parameter. */
    )
{
    if ( dwReason == DLL_PROCESS_ATTACH )
    {
        DBGMSG(IIC_FUNC,(L"I2C : Process Attach\r\n"));
    }

    if ( dwReason == DLL_PROCESS_DETACH )
    {
        DBGMSG(IIC_FUNC,(L"I2C: Process Detach\r\n"));
    }

    return(TRUE);
}


BOOL
I2C_Deinit(
   PI2C_PUBLIC_CONTEXT pPublicCtxt
   )
{
	DBGMSG(IIC_FUNC, (TEXT("I2C : ++ I2C_Deinit()\r\n")));

	if(pPublicCtxt != NULL)
	{
     	if(pPublicCtxt->pI2CCtrlCtxt)
		{
		    pPublicCtxt->pI2CCtrlCtxt->PowerDown();
		}

		if(pPublicCtxt->pI2CCtrlCtxt)
		{
			pPublicCtxt->pI2CCtrlCtxt->Close();	
			delete pPublicCtxt->pI2CCtrlCtxt;
		}

		if(pPublicCtxt->pI2CReg)
		{
			MmUnmapIoSpace(pPublicCtxt->pI2CReg, sizeof(SXXXXXX_I2C_REG));
		}

		if(pPublicCtxt->pGPIOReg)
		{
			MmUnmapIoSpace(pPublicCtxt->pGPIOReg, sizeof(SXXXXXX_GPIO_REG));
		}

		if(pPublicCtxt->pSYSCLKReg)
		{
			MmUnmapIoSpace(pPublicCtxt->pSYSCLKReg, sizeof(SXXXXXX_SYSCLK_REG));
		}
        if(pPublicCtxt->pBSPArgs)
		{
			MmUnmapIoSpace((PVOID)pPublicCtxt->pBSPArgs, sizeof(SXXXXXX_BSP_ARG));
		}
		
		if(LocalFree(pPublicCtxt))
		{
			ERRMSG((L"[I2C:E] Deallocating Public Context is failed\r\n"));
		}
	}

	DBGMSG(IIC_FUNC, (TEXT("I2C : -- I2C_Deinit()\r\n")));

	return TRUE;
}

PI2C_PUBLIC_CONTEXT
I2C_Init(
   PVOID Context
   )
{
	LPTSTR 					lpActivePath = (LPTSTR)Context; // HKLM\Drivers\Active\xx
	PHYSICAL_ADDRESS    	ioPhysicalBase = {0,0};
	PI2C_PUBLIC_CONTEXT		pPublicCtxt = NULL;
	BOOL					bInitSuccess = TRUE;
	CRegistryEdit 			cRegActive(lpActivePath);
	DWORD					dwSlaveAddress;
	DWORD                   dwMethod;
	DWORD					dwThreadPriority;
	
	DBGMSG(IIC_FUNC, (L"[I2C:F] +++ I2C_Init(ACTIVE PATH : %s, VERSION INFO : %d)\r\n", lpActivePath, DRIVER_VERSION_INFO));
	
	do
	{
		if(!(pPublicCtxt = (PI2C_PUBLIC_CONTEXT)LocalAlloc(LPTR, sizeof(I2C_PUBLIC_CONTEXT))))
		{
			ERRMSG((L"[I2C:E] Allocating Public Context is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}
		
        pPublicCtxt->pHead          = NULL;
        pPublicCtxt->pTail          = NULL;
	    pPublicCtxt->pI2CCtrlCtxt 	= NULL;
		
		if(!cRegActive.IsKeyOpened())
		{
			ERRMSG((L"[I2C:E] Opening ActivePath registry is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}

		if(!cRegActive.GetRegValue(L"SlaveAddress", (LPBYTE)&dwSlaveAddress, sizeof(dwSlaveAddress)))
		{
			ERRMSG((L"[I2C:E] Getting registry \"SlaveAddress\" is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}		

		ioPhysicalBase.LowPart = SXXXXXX_BASE_REG_PA_GPIO;
		pPublicCtxt->pGPIOReg = (PSXXXXXX_GPIO_REG)MmMapIoSpace(ioPhysicalBase, sizeof(SXXXXXX_GPIO_REG), FALSE);
		if (pPublicCtxt->pGPIOReg == NULL)
		{
			ERRMSG((L"[I2C:E] pGPIOReg mapping is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}

		ioPhysicalBase.LowPart = SXXXXXX_BASE_REG_PA_SYSCLK;
		pPublicCtxt->pSYSCLKReg = (PSXXXXXX_SYSCLK_REG)MmMapIoSpace(ioPhysicalBase, sizeof(SXXXXXX_SYSCLK_REG), FALSE);
		if (pPublicCtxt->pSYSCLKReg == NULL)
		{
			ERRMSG((L"[I2C:E] pSYSCLKReg mapping is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}	

		ioPhysicalBase.LowPart = SXXXXXX_BASE_REG_PA_BSPARG;
		pPublicCtxt->pBSPArgs = (PSXXXXXX_BSP_ARG)MmMapIoSpace(ioPhysicalBase, sizeof(SXXXXXX_BSP_ARG), FALSE);
		if (pPublicCtxt->pBSPArgs == NULL)
		{
			ERRMSG((L"[I2C:E] pBSPArgs mapping is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}

		if(!cRegActive.GetRegValue(L"Channel", (LPBYTE)&pPublicCtxt->dwChannel, sizeof(pPublicCtxt->dwChannel)))
		{
			ERRMSG((L"[I2C:E] Getting registry \"Channel\" is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}		

		if(!cRegActive.GetRegValue(L"Mode", (LPBYTE)&dwMethod, sizeof(dwMethod)))
		{
			ERRMSG((L"[I2C:E] Getting registry \"Mode\" is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}	

		if(!cRegActive.GetRegValue(L"Priority256", (LPBYTE)&dwThreadPriority, sizeof(dwThreadPriority)))
		{
			ERRMSG((L"[I2C:E] Getting registry \"Priority256\" is failed but DEFAULT_THREAD_PRIORITY IS APPLIED\r\n"));
			dwThreadPriority = DEFAULT_THREAD_PRIORITY;
		}	

        DBGMSG(TRUE, (L"[I2C:I] I2C Channel %d is open\r\n", pPublicCtxt->dwChannel));

        if(IS_HWI2C(pPublicCtxt->dwChannel))
        {
            if(pPublicCtxt->dwChannel == 0)
                ioPhysicalBase.LowPart = SXXXXXX_BASE_REG_PA_I2C0;
            else if(pPublicCtxt->dwChannel == 1)
                ioPhysicalBase.LowPart = SXXXXXX_BASE_REG_PA_I2C1;
            else if(pPublicCtxt->dwChannel == 2)
                ioPhysicalBase.LowPart = SXXXXXX_BASE_REG_PA_I2C2;
            else
                ioPhysicalBase.LowPart = SXXXXXX_BASE_REG_PA_I2C3;

            pPublicCtxt->pI2CReg = (PSXXXXXX_I2C_REG)MmMapIoSpace(ioPhysicalBase, sizeof(SXXXXXX_I2C_REG), FALSE);
			if (pPublicCtxt->pI2CReg == NULL)
			{
				ERRMSG((L"[I2C:E] pI2CReg%d mapping is failed\r\n", pPublicCtxt->dwChannel));
				bInitSuccess = FALSE;
				break;
			}	

			pPublicCtxt->pI2CCtrlCtxt = new I2CHWContext();
			pPublicCtxt->pI2CCtrlCtxt->Open(pPublicCtxt->dwChannel, dwMethod, dwThreadPriority, (WORD)dwSlaveAddress, pPublicCtxt->pI2CReg, pPublicCtxt->pGPIOReg, pPublicCtxt->pSYSCLKReg, pPublicCtxt->pBSPArgs);
        }
		else
		{
			pPublicCtxt->pI2CCtrlCtxt = new I2CEmulContext();
			pPublicCtxt->pI2CCtrlCtxt->Open(pPublicCtxt->dwChannel, dwMethod, dwThreadPriority, 0, NULL, pPublicCtxt->pGPIOReg, NULL, pPublicCtxt->pBSPArgs);
		}	

		pPublicCtxt->pI2CCtrlCtxt->PowerUp();

        g_hProc = (HANDLE)GetCurrentProcessId();
		
	} while(FALSE);
	
	DBGMSG(IIC_FUNC, (L"[I2C:F] --- I2C_Init\r\n"));

	if(bInitSuccess)
	{		
		return pPublicCtxt;
	}

	I2C_Deinit(pPublicCtxt);

	return NULL;
}

PI2C_PRIVATE_CONTEXT
I2C_Open(
   PI2C_PUBLIC_CONTEXT 	pPublicCtxt,       // context returned by I2C_Init.
   DWORD        		AccessCode, // @parm access code
   DWORD        		ShareMode   // @parm share mode
   )
{
	UNREFERENCED_PARAMETER(ShareMode);
	UNREFERENCED_PARAMETER(AccessCode);

	PI2C_PRIVATE_CONTEXT 				pPrivateCtxt;
	BOOL								bInitSuccess = TRUE;

	DBGMSG(IIC_FUNC, (L"[I2C:F] +++ I2C_Open(%#x, %#x, %#x)\r\n",pPublicCtxt, AccessCode, ShareMode));

	do 
	{
		if(!(pPrivateCtxt = (PI2C_PRIVATE_CONTEXT)LocalAlloc(LPTR, sizeof(I2C_PRIVATE_CONTEXT))))
		{
			ERRMSG((L"[I2C:E] Allocating I2C Private Context is failed\r\n"));
			bInitSuccess = FALSE;
			break;
		}

        pPrivateCtxt->pNext         = NULL;
        pPrivateCtxt->pPrev         = NULL;
        pPrivateCtxt->pPublicCtxt   = pPublicCtxt;
		pPrivateCtxt->pI2CObj = new I2CObject();

        if(pPublicCtxt->pTail)
        {
            pPublicCtxt->pTail->pNext   = pPrivateCtxt;
            pPrivateCtxt->pPrev         = pPublicCtxt->pTail;
            pPublicCtxt->pTail          = pPrivateCtxt;
        }
        else
        {
            pPublicCtxt->pHead = pPrivateCtxt;
            pPublicCtxt->pTail = pPrivateCtxt;
        }
	} while(FALSE);
	
	DBGMSG(IIC_FUNC, (L"[I2C:F] --- I2C_Open\r\n"));

	if(bInitSuccess) return pPrivateCtxt;
	return NULL;
}


BOOL
I2C_Close(
   PI2C_PRIVATE_CONTEXT pPrivateCtxt
   )
{
    PI2C_PUBLIC_CONTEXT pPublicCtxt = pPrivateCtxt->pPublicCtxt;
    BOOL				bCloseSuccess = TRUE;
    
	DBGMSG(IIC_FUNC,(L"[I2C:F] +++ I2C_Close(%#x)\r\n", pPrivateCtxt));

    do
    {
	if(pPrivateCtxt->pI2CObj)
    	{
		delete pPrivateCtxt->pI2CObj;
            pPrivateCtxt->pI2CObj = NULL;
    	}

        if(pPublicCtxt->pHead == pPrivateCtxt)
        {
            pPublicCtxt->pHead = pPrivateCtxt->pNext;
        }

        if(pPublicCtxt->pTail == pPrivateCtxt)
        {
            pPublicCtxt->pTail = pPrivateCtxt->pPrev;
        }

        if(pPrivateCtxt->pNext)
        {
            pPrivateCtxt->pNext->pPrev = pPrivateCtxt->pPrev;
        }

        if(pPrivateCtxt->pPrev)
        {
            pPrivateCtxt->pPrev->pNext = pPrivateCtxt->pNext;
        }

        pPrivateCtxt->pPrev = NULL;
        pPrivateCtxt->pNext = NULL;

	if(LocalFree(pPrivateCtxt))
	{
		ERRMSG((L"[I2C:E] Deallocating I2C Private Context is failed\r\n"));
            bCloseSuccess = FALSE;
            break;
	}
    } while(FALSE);
	
	DBGMSG(IIC_FUNC,(L"[I2C:F] --- I2C_Close\r\n"));
    
	return bCloseSuccess;
}


ULONG
I2C_Write(
   PI2C_PRIVATE_CONTEXT pPrivateCtxt,
   PUCHAR 				pBuffer,
   ULONG  				BufferLength
   )
{
	return 0;
}


ULONG
I2C_Read(
   PI2C_PRIVATE_CONTEXT pPrivateCtxt,
   PUCHAR 				pBuffer,
   ULONG  				BufferLength
   )
{
	return 0;
}


ULONG
I2C_Seek(
   PVOID Context,
   LONG  Position,
   DWORD Type
   )
{
    return (ULONG)-1;
}


BOOL
I2C_PowerUp(
    PI2C_PUBLIC_CONTEXT 	pPublicCtxt
   )
{
	BOOL bRtn = TRUE;
	DBGMSG(IIC_PM,(TEXT("[I2C:PM] I2C PowerUp+\r\n")));
	pPublicCtxt->pI2CCtrlCtxt->PowerUp();
	DBGMSG(IIC_PM,(TEXT("[I2C:PM] I2C PowerUp-\r\n")));
	return bRtn;
}


BOOL
I2C_PowerDown(
   PI2C_PUBLIC_CONTEXT 	pPublicCtxt
   )
{
	BOOL bRtn = TRUE;
	DBGMSG(IIC_PM,(TEXT("[I2C:PM] I2C PowerDown+\r\n")));
	pPublicCtxt->pI2CCtrlCtxt->PowerDown();
	DBGMSG(IIC_PM,(TEXT("[I2C:PM] I2C PowerDown-\r\n")));
	return bRtn;
}

#define I2CObj		(*pI2CObj)	

BOOL
I2C_FastRead(
    PVOID 	        pPrivateCtxt,
    PI2C_READ_DESC  pReadDesc
    )
{
    BOOL 					bResult         = TRUE;
	PI2CObject				pI2CObj 		= ((PI2C_PRIVATE_CONTEXT)pPrivateCtxt)->pI2CObj;
	PI2C_PUBLIC_CONTEXT 	pPublicCtxt 	= ((PI2C_PRIVATE_CONTEXT)pPrivateCtxt)->pPublicCtxt;
	BYTE					btACK           = I2C_ACK;
	BOOL					bStartTransfer;
    HRESULT hr;
    PBYTE     MappedEmbedded;
    PBYTE     Marshalled;
	I2CObj.Lock();

    DBGMSG(IIC_FUNC,(L"[I2C:I] +++I2C_FastRead\r\n"));	
    
	bStartTransfer = FALSE;
    #if WINCE_API
    {
        hr = CeOpenCallerBuffer((PVOID*) &MappedEmbedded, pReadDesc->IO_pbtData, pReadDesc->IN_dwData, ARG_IO_PTR, FALSE);
        if(hr != S_OK)
        {
            bResult = FALSE;
        }

        hr = CeAllocAsynchronousBuffer((PVOID*) &Marshalled, MappedEmbedded, pReadDesc->IN_dwData, ARG_IO_PTR);
        if(hr != S_OK)
        {
            bResult = FALSE;
        }        
    }
    #else
	{
        I2CObj.pRxBuffer = (PBYTE) MapCallerPtr(pReadDesc->IO_pbtData, pReadDesc->IN_dwData);
	}
#endif	
	
	I2CObj.pRxBuffer = (PBYTE) pReadDesc->IO_pbtData;	
	I2CObj.dwRxCount = pReadDesc->IN_dwData;

	if(I2CObj.dwRxCount == 1)
		I2CObj.Begin().SetStart(I2CMODE_MASTER_READ).DetectSlave().GetData().SetNAK();
	else
		I2CObj.Begin().SetStart(I2CMODE_MASTER_READ).DetectSlave().GetData().SetACK().SetLoop(2, I2CObj.dwRxCount-2).GetData().SetNAK();

	if(pReadDesc->IN_bStop)
	{
		bResult = I2CObj.SetStop().End();
		if(bResult)
			bStartTransfer = TRUE;
	}
	else
		bResult = I2CObj.End();    

	if(bStartTransfer)
	{
		pPublicCtxt->pI2CCtrlCtxt->Lock();
		bResult = pPublicCtxt->pI2CCtrlCtxt->StartTransfer(pI2CObj);
		pPublicCtxt->pI2CCtrlCtxt->Unlock();
	}

	I2CObj.Unlock();

#ifdef TEST_MODE	
	if(bResult) DBGMSG(IIC_INFO,(L"[I2C:I] IOCTL RESULT : SUCCESS\r\n"));	
	else 		DBGMSG(IIC_INFO,(L"[I2C:I] IOCTL RESULT : FAIL\r\n"));	
#endif

    DBGMSG(IIC_FUNC,(L"[I2C:I] ---I2C_FastRead\r\n"));

	return bResult;            
}

BOOL
I2C_FastWrite(
    PVOID 	        pPrivateCtxt,
    PI2C_WRITE_DESC pWriteDesc
    )
{
    BOOL 					bResult         = TRUE;
	PI2CObject				pI2CObj 		= ((PI2C_PRIVATE_CONTEXT)pPrivateCtxt)->pI2CObj;
	PI2C_PUBLIC_CONTEXT 	pPublicCtxt 	= ((PI2C_PRIVATE_CONTEXT)pPrivateCtxt)->pPublicCtxt;
	BYTE					btACK           = I2C_ACK;
	BOOL					bStartTransfer;
    HRESULT hr;
    PBYTE     MappedEmbedded;
    PBYTE     Marshalled;
	I2CObj.Lock();

    DBGMSG(IIC_FUNC,(L"[I2C:I] +++I2C_FastWrite\r\n"));	
    
	bStartTransfer = FALSE;

    #if WINCE_API
	{
        hr = CeOpenCallerBuffer((PVOID*) &MappedEmbedded, pWriteDesc->IO_pbtData, pWriteDesc->IN_dwData, ARG_IO_PTR, FALSE);
        if(hr != S_OK)
        {
            bResult = FALSE;
        }

        hr = CeAllocAsynchronousBuffer((PVOID*) &Marshalled, MappedEmbedded, pWriteDesc->IN_dwData, ARG_IO_PTR);
        if(hr != S_OK)
        {
            bResult = FALSE;
        }
    }
    #else
    {
	I2CObj.pTxBuffer = (PBYTE) MapCallerPtr(pWriteDesc->IO_pbtData, pWriteDesc->IN_dwData);
	}
#endif

	I2CObj.pTxBuffer = (PBYTE) pWriteDesc->IO_pbtData;	
	I2CObj.dwTxCount = pWriteDesc->IN_dwData;
		I2CObj.Begin().SetStart(I2CMODE_MASTER_WRITE).DetectSlave().SetData().GetACK(&btACK).SetLoop(2, I2CObj.dwTxCount-1);

	if(pWriteDesc->IN_bStop)
	{
		bResult = I2CObj.SetStop().End();
		if(bResult)
			bStartTransfer = TRUE;
	}
	else
		bResult = I2CObj.End();

	if(bStartTransfer)
	{
		pPublicCtxt->pI2CCtrlCtxt->Lock();
		bResult = pPublicCtxt->pI2CCtrlCtxt->StartTransfer(pI2CObj);
		pPublicCtxt->pI2CCtrlCtxt->Unlock();

		if(btACK == I2C_NAK)
		{
			bResult = FALSE;
			ERRMSG((L"[I2C:E] Slave didn't send ACK\r\n"));
		}
	}

	I2CObj.Unlock();

#ifdef TEST_MODE	
	if(bResult) DBGMSG(IIC_INFO,(L"[I2C:I] IOCTL RESULT : SUCCESS\r\n"));	
	else 		DBGMSG(IIC_INFO,(L"[I2C:I] IOCTL RESULT : FAIL\r\n"));	
#endif

    DBGMSG(IIC_FUNC,(L"[I2C:I] ---I2C_FastWrite\r\n"));	

	return bResult;            
}

CEDEVICE_POWER_STATE
I2C_SetPowerState(
    PI2C_PUBLIC_CONTEXT pPublicCtxt, 
    CEDEVICE_POWER_STATE requestState
    )
{
	BOOL bRtn = TRUE;

	__try
	{
		switch(requestState)
		{
			case D0:
			case D1:
			case D2:
			case D3:
				requestState = D0;
				break;
			case D4:
				requestState = D4;
				break;
			default:
			requestState = pPublicCtxt->pI2CCtrlCtxt->GetPowerState();
			break;
		}
	
	    DBGMSG(IIC_PM,(TEXT("I2C : I2C PowerUp+\r\n")));

		// NOT POWER CONTROL
		if(pPublicCtxt->pI2CCtrlCtxt->GetPowerState() != requestState)
		{
			if(requestState == D0)
			{
				bRtn = pPublicCtxt->pI2CCtrlCtxt->PowerUp();
				/*
                pItem = pPublicCtxt->pHead;
                while(pItem)
                {
                    if(pItem->pI2CObj)
                    {
                        pItem->pI2CObj->Unlock();
    					DBGMSG(1, (L"0x%X(SlaveAddr : 0x%X, Speed %d) is unlocked\r\n", pItem->pI2CObj, pItem->pI2CObj->GetSlaveAddr(), pItem->pI2CObj->GetClockSpeed()));
                    }
                    pItem = pItem->pNext;
				}*/

				/*
				if(pPublicCtxt->pI2CCtrlCtxt->GetMethod() == I2C_METHOD_INTERRUPT)
	            {
					DWORD dwSysIntr = pPublicCtxt->pI2CCtrlCtxt->GetSysIntr();
					if ((dwSysIntr != SYSINTR_UNDEFINED) && !KernelIoControl(IOCTL_HAL_DISABLE_WAKE, &dwSysIntr, sizeof(DWORD), NULL, 0, NULL))
	                {
						DBGMSG(IIC_PM,(TEXT("[I2C:PM] IOCTL_HAL_DISABLE_WAKE is failed\r\n")));
	                }
				}*/
            }
			else if(requestState == D4)
            {
				/*pItem = pPublicCtxt->pHead;
                while(pItem)
                {
                    if(pItem->pI2CObj)
                    {
                        pItem->pI2CObj->Lock();
                        pItem->pI2CObj->ClrStateFIFO();
            						DBGMSG(1, (L"0x%X(SlaveAddr : 0x%X, Speed %d) is locked\r\n", pItem->pI2CObj, pItem->pI2CObj->GetSlaveAddr(), pItem->pI2CObj->GetClockSpeed()));
                    }
                    pItem = pItem->pNext;
				}*/
				/*
				if(pPublicCtxt->pI2CCtrlCtxt->GetMethod() == I2C_METHOD_INTERRUPT)
	            {
					DWORD dwSysIntr = pPublicCtxt->pI2CCtrlCtxt->GetSysIntr();
					if ((dwSysIntr != SYSINTR_UNDEFINED) && !KernelIoControl(IOCTL_HAL_DISABLE_WAKE, &dwSysIntr, sizeof(DWORD), NULL, 0, NULL))
					{
						DBGMSG(IIC_PM,(TEXT("[I2C:PM] IOCTL_HAL_DISABLE_WAKE is failed\r\n")));
	                }
				}*/
				bRtn = pPublicCtxt->pI2CCtrlCtxt->PowerDown();
			}
		} 
	}
	__except(pPublicCtxt->pI2CCtrlCtxt->I2CProcessException(GetExceptionInformation()))
	{
		ERRMSG((L"[I2C:PM] I2C_SetPowerState EXCEPTION\r\n"));
	}
	
	return requestState;
}

BOOL
I2C_IOControl(
    PI2C_PRIVATE_CONTEXT 	pPrivateCtxt,
    DWORD 					dwCode,
    PBYTE 					pBufIn,
    DWORD 					dwLenIn,
    PBYTE 					pBufOut,
    DWORD 					dwLenOut,
    PDWORD 					pdwActualOut
   )
{
	BOOL 					bResult         = TRUE;
	PI2CObject				pI2CObj 		= pPrivateCtxt->pI2CObj;
	PI2C_PUBLIC_CONTEXT 	pPublicCtxt 	= pPrivateCtxt->pPublicCtxt;
	PI2C_PRIVATE_CONTEXT 	pList 			= pPublicCtxt->pHead;
	PI2C_INIT_DESC 			pInitDesc;
	PI2C_READ_DESC 			pReadDesc;
	PI2C_WRITE_DESC 		pWriteDesc;
	PI2C_SS_READ_DESC		pSSReadDesc;
	PI2C_SS_WRITE_DESC		pSSWriteDesc;
	BYTE					btACK           = I2C_ACK;
	BOOL					bReady          = TRUE;
	BOOL					bStartTransfer;
    HRESULT hr;
    PBYTE     MappedEmbedded;
    PBYTE     Marshalled;    

	I2CObj.Lock();
	bStartTransfer = FALSE;
	
	switch (dwCode) 
	{
        case IOCTL_I2C_INIT:
			DBGMSG(IIC_INFO,(TEXT("[I2C:I] IOCTL_I2C_INIT\r\n")));
			if( dwLenIn != sizeof(I2C_INIT_DESC) ) {
				ERRMSG((L"[I2C:E] I2C_INIT_DESC size is INVALID\r\n"));
				bResult = FALSE;
				break;
			}
			
			pInitDesc = (PI2C_INIT_DESC)pBufIn;

			if(IS_HWI2C(pPrivateCtxt->pPublicCtxt->dwChannel))
				pInitDesc->OUT_dwActualSpeed = I2CObj.Init(FALSE, pInitDesc->IN_wSlaveAddr, pInitDesc->IN_dwClockSpeed); // HW
			else
				pInitDesc->OUT_dwActualSpeed = I2CObj.Init(TRUE, pInitDesc->IN_wSlaveAddr, pInitDesc->IN_dwClockSpeed); // Emulation
			
			DBGMSG(IIC_INFO, (L"[I2C:I] Actual Spead : %dKhz\r\n", pInitDesc->OUT_dwActualSpeed));

#ifdef TEST_MODE
			DBGMSG(IIC_INFO, (L"[I2C:I] %d Channel List : \r\n", pPublicCtxt->dwChannel));
			while(pList)
			{
				DBGMSG(IIC_INFO, (L"0x%X(SlaveAddr : 0x%X, Speed %d)\r\n", pList->pI2CObj, pList->pI2CObj->GetSlaveAddr(), pList->pI2CObj->GetClockSpeed()));
				pList = pList->pNext;
			}
#endif
			break;
			
        case IOCTL_I2C_GENERAL_READ:
			if( dwLenIn != sizeof(I2C_READ_DESC) ) {
				ERRMSG((L"[I2C:E] PI2C_READ_DESC size is INVALID\n"));
				bResult = FALSE;
				break;
			}

			pReadDesc = (PI2C_READ_DESC)pBufIn;

                #if WINCE_API
                {
                    hr = CeOpenCallerBuffer((PVOID*) &MappedEmbedded, pReadDesc->IO_pbtData, pReadDesc->IN_dwData, ARG_IO_PTR, FALSE);
                    if(hr != S_OK)
                    {
                        bResult = FALSE;
                    }

                    hr = CeAllocAsynchronousBuffer((PVOID*) &Marshalled, MappedEmbedded, pReadDesc->IN_dwData, ARG_IO_PTR);
                    if(hr != S_OK)
                    {
                        bResult = FALSE;
                    }                    
                }
                #else
			{
			I2CObj.pRxBuffer = (PBYTE) MapCallerPtr(pReadDesc->IO_pbtData, pReadDesc->IN_dwData);
			}
#endif			
                
			I2CObj.pRxBuffer = (PBYTE) pReadDesc->IO_pbtData;						
			I2CObj.dwRxCount = pReadDesc->IN_dwData;
			DBGMSG(IIC_INFO,(TEXT("[I2C:I] IOCTL_I2C_GENERAL_READ(size : %d)\r\n"), I2CObj.dwRxCount));

			if(I2CObj.dwRxCount == 1)
				I2CObj.Begin().SetStart(I2CMODE_MASTER_READ).DetectSlave().GetData().SetNAK();
			else
				I2CObj.Begin().SetStart(I2CMODE_MASTER_READ).DetectSlave().GetData().SetACK().SetLoop(2, I2CObj.dwRxCount-2).GetData().SetNAK();

			if(pReadDesc->IN_bStop)
			{
				bResult = I2CObj.SetStop().End();
				if(bResult)
					bStartTransfer = TRUE;
			}
			else
				bResult = I2CObj.End();
			break;

		case IOCTL_I2C_GENERAL_WRITE:
			if( dwLenIn != sizeof(I2C_WRITE_DESC) ) {
				ERRMSG((L"[I2C:E] I2C_WRITE_DESC size is INVALID\n"));
				bResult = FALSE;
				break;
			}

			pWriteDesc = (PI2C_WRITE_DESC)pBufIn;

                    #if WINCE_API        
                    {
                        hr = CeOpenCallerBuffer((PVOID*) &MappedEmbedded, pWriteDesc->IO_pbtData, pWriteDesc->IN_dwData, ARG_IO_PTR, FALSE);
                        if(hr != S_OK)
                        {
                            bResult = FALSE;
                        }

                        hr = CeAllocAsynchronousBuffer((PVOID*) &Marshalled, MappedEmbedded, pWriteDesc->IN_dwData, ARG_IO_PTR);
                        if(hr != S_OK)
                        {
                            bResult = FALSE;
                        }
                    }
                    #else
			{
                        I2CObj.pTxBuffer = (PBYTE) MapCallerPtr(pWriteDesc->IO_pbtData, pWriteDesc->IN_dwData);
			}
#endif

			I2CObj.pTxBuffer = (PBYTE) pWriteDesc->IO_pbtData;			
			I2CObj.dwTxCount = pWriteDesc->IN_dwData;
			DBGMSG(IIC_INFO,(TEXT("[I2C:I] IOCTL_I2C_GENERAL_WRITE(size : %d)\r\n"), I2CObj.dwTxCount));
			
			I2CObj.Begin().SetStart(I2CMODE_MASTER_WRITE).DetectSlave().SetData().GetACK(&btACK).SetLoop(2, I2CObj.dwTxCount-1);

			if(pWriteDesc->IN_bStop)
			{
				bResult = I2CObj.SetStop().End();
				if(bResult)
					bStartTransfer = TRUE;
			}
			else
				bResult = I2CObj.End();
			break;

		case IOCTL_I2C_EDIDSEGMENT_WRITE:
			if( dwLenIn != sizeof(I2C_WRITE_DESC) ) {
				ERRMSG((L"[I2C:E] I2C_WRITE_DESC size is INVALID\n"));
				bResult = FALSE;
				break;
			}

			pWriteDesc = (PI2C_WRITE_DESC)pBufIn;
			I2CObj.dwTxCount = pWriteDesc->IN_dwData;
			DBGMSG(IIC_INFO,(TEXT("[I2C:I] IOCTL_I2C_EDIDSEGMENT_WRITE(size : %d)\r\n"), I2CObj.dwTxCount));
			
			I2CObj.Begin().SetStartCustom(I2CMODE_MASTER_WRITE).SetData().SetLoop(1, I2CObj.dwTxCount-1);   //Segment Write
			break;
		case IOCTL_I2C_SLOW_SLAVE_READ:	
			if( dwLenIn != sizeof(I2C_SS_READ_DESC) ) {
				ERRMSG((L"[I2C:E] I2C_SS_READ_DESC size is INVALID\n"));
				bResult = FALSE;
				break;
			}

			pSSReadDesc = (PI2C_SS_READ_DESC)pBufIn;
 
                    #if WINCE_API
			{
                        hr = CeOpenCallerBuffer((PVOID*) &MappedEmbedded, pSSReadDesc->IO_pbtData, pSSReadDesc->IN_dwData, ARG_IO_PTR, FALSE);
                        if(hr != S_OK)
                        {
                            bResult = FALSE;
                        }

                        hr = CeAllocAsynchronousBuffer((PVOID*) &Marshalled, MappedEmbedded, pSSReadDesc->IN_dwData, ARG_IO_PTR);
                        if(hr != S_OK)
                        {
                            bResult = FALSE;
			}
                        
                    }
                    #else
                    {
        			I2CObj.pRxBuffer = (PBYTE) MapCallerPtr(pSSReadDesc->IO_pbtData, pSSReadDesc->IN_dwData);
                    }

#endif			

			I2CObj.pRxBuffer = (PBYTE) pSSReadDesc->IO_pbtData;			
			I2CObj.dwRxCount = pSSReadDesc->IN_dwData;
			DBGMSG(IIC_INFO,(TEXT("[I2C:I] IOCTL_I2C_SLOW_SLAVE_READ(size : %d)\r\n"), I2CObj.dwRxCount));

			if(I2CObj.dwRxCount == 1)
				I2CObj.Begin().SetStart(I2CMODE_MASTER_READ).DetectSlave().GetData().SetNAK().WaitToReady(pSSReadDesc->IN_dwTime, &bReady);
			else
				I2CObj.Begin().SetStart(I2CMODE_MASTER_READ).DetectSlave().GetData().SetACK().WaitToReady(pSSReadDesc->IN_dwTime, &bReady).SetLoop(3, I2CObj.dwRxCount-2).GetData().SetNAK().WaitToReady(pSSReadDesc->IN_dwTime, &bReady);

			if(pSSReadDesc->IN_bStop)
			{
				bResult = I2CObj.SetStop().End();
				if(bResult)
					bStartTransfer = TRUE;
			}
			else
				bResult = I2CObj.End();
			break;
			
		case IOCTL_I2C_SLOW_SLAVE_WRITE:
			if( dwLenIn != sizeof(I2C_SS_WRITE_DESC) ) {
				ERRMSG((L"[I2C:E] I2C_SS_WRITE_DESC size is INVALID\n"));
				bResult = FALSE;
				break;
			}

			pSSWriteDesc = (PI2C_SS_WRITE_DESC)pBufIn;

                    #if WINCE_API
                    {
                        hr = CeOpenCallerBuffer((PVOID*) &MappedEmbedded, pSSWriteDesc->IO_pbtData, pSSWriteDesc->IN_dwData, ARG_IO_PTR, FALSE);
                        if(hr != S_OK)
                        {
                            bResult = FALSE;
                        }

                        hr = CeAllocAsynchronousBuffer((PVOID*) &Marshalled, MappedEmbedded, pSSWriteDesc->IN_dwData, ARG_IO_PTR);
                        if(hr != S_OK)
			{
                            bResult = FALSE;
                        }                        
                    }
                    #else
                    {
                        I2CObj.pTxBuffer = (PBYTE) MapCallerPtr(pSSWriteDesc->IO_pbtData, pSSWriteDesc->IN_dwData);
			}
#endif			
			
                    I2CObj.pTxBuffer = (PBYTE) pSSWriteDesc->IO_pbtData;			
			I2CObj.dwTxCount = pSSWriteDesc->IN_dwData;
			DBGMSG(IIC_INFO,(TEXT("[I2C:I] IOCTL_I2C_SLOW_SLAVE_WRITE(size : %d)\r\n"), I2CObj.dwTxCount));
			
			I2CObj.Begin().SetStart(I2CMODE_MASTER_WRITE).DetectSlave().SetData().GetACK(&btACK).WaitToReady(pSSWriteDesc->IN_dwTime, &bReady).SetLoop(3, I2CObj.dwTxCount-1);
			
			if(pSSWriteDesc->IN_bStop)
			{
				bResult = I2CObj.SetStop().End();
				if(bResult)
					bStartTransfer = TRUE;
			}
			else
				bResult = I2CObj.End();
			break;
			
		case IOCTL_I2C_VIRTUAL_READ:
			if( dwLenIn != sizeof(I2C_VT_READ_DESC) ) {
				ERRMSG((L"[I2C:E] I2C_VT_READ_DESC size is INVALID\n"));
				bResult = FALSE;
				break;
			}
/*
			pReadDesc = (PI2C_READ_DESC)pBufIn;
			I2CObj.dwRxCount = pReadDesc->IN_dwData;
            
			if(I2CObj.dwRxCount == 1)
				I2CObj.Begin().SetStart(I2CMODE_MASTER_READ).DetectSlave().GetData().SetNAK();
			else
				I2CObj.Begin().SetStart(I2CMODE_MASTER_READ).DetectSlave().GetData().SetACK().SetLoop(2, I2CObj.dwRxCount-2).GetData().SetNAK();

			if(pReadDesc->IN_bStop)
			{
				bResult = I2CObj.SetStop().End();
                if(bResult)
                {
                    
                }
			}
			else
				bResult = I2CObj.End();*/
			break;

		case IOCTL_I2C_VIRTUAL_WRITE:
			if( dwLenIn != sizeof(I2C_VT_WRITE_DESC) ) {
				ERRMSG((L"[I2C:E] I2C_VT_WRITE_DESC size is INVALID\n"));
				bResult = FALSE;
				break;
			}
/*
			pWriteDesc = (PI2C_WRITE_DESC)pBufIn;
			I2CObj.pTxBuffer = (PBYTE) MapCallerPtr(pWriteDesc->IO_pbtData, pWriteDesc->IN_dwData);
			I2CObj.dwTxCount = pWriteDesc->IN_dwData;
			DBGMSG(IIC_INFO,(TEXT("[I2C:I] IOCTL_I2C_GENERAL_WRITE(size : %d)\r\n"), I2CObj.dwTxCount));
			
			I2CObj.Begin().SetStart(I2CMODE_MASTER_WRITE).DetectSlave().SetData().GetACK(&btACK).SetLoop(2, I2CObj.dwTxCount-1);

			if(pWriteDesc->IN_bStop)
			{
				bResult = I2CObj.SetStop().End();
				if(bResult)
					bStartTransfer = TRUE;
			}
			else
				bResult = I2CObj.End();*/
			break;

        case IOCTL_I2C_GET_FASTCALL:
			if ( (dwLenOut < sizeof(I2C_FASTCALL)) || !pBufOut ) {
                ERRMSG((L"[I2C:E] IOCTL_I2C_GET_FASTCALL size is INVALID\r\n"));
                bResult = FALSE;
                break;
            }

            // Check caller process & fail if they are not in device.exe!
            if (GetCallerProcess() != g_hProc ) {
                ERRMSG((L"[I2C:E] ERROR_ACCESS_DENIED: Caller(0x%X) != Current(0x%X)\n", GetCallerProcess(), g_hProc));
                bResult = FALSE;
                break;
            }
            
            ((PI2C_FASTCALL)pBufOut)->Context  = pPrivateCtxt;
            ((PI2C_FASTCALL)pBufOut)->I2CRead  = I2C_FastRead;
            ((PI2C_FASTCALL)pBufOut)->I2CWrite = I2C_FastWrite;

            if (pdwActualOut)
                *pdwActualOut = sizeof(I2C_FASTCALL);
            break;

        case IOCTL_POWER_CAPABILITIES:
            DBGMSG(IIC_PM, (L"[I2C:PM] +++IOCTL_POWER_CAPABILITIES\r\n"));

			if (pBufOut && pdwActualOut && (dwLenOut >= sizeof(POWER_CAPABILITIES)))
			{
				__try
				{
					PPOWER_CAPABILITIES PowerCaps = (PPOWER_CAPABILITIES)pBufOut;
					memset(PowerCaps, 0, sizeof(*PowerCaps));
					PowerCaps->DeviceDx = pPublicCtxt->pI2CCtrlCtxt->GetSupportablePowerState();
					*pdwActualOut = sizeof(*PowerCaps);
				}
				__except(pPublicCtxt->pI2CCtrlCtxt->I2CProcessException(GetExceptionInformation()))
				{
					ERRMSG((L"[I2C:PM] IOCTL_POWER_CAPABILITIES EXCEPTION\r\n"));
                    			bResult = FALSE;
				}
			}
            
            DBGMSG(IIC_PM, (L"[I2C:PM] ---IOCTL_POWER_CAPABILITIES\r\n"));
            break;
            
        case IOCTL_POWER_QUERY:
            DBGMSG(IIC_PM, (L"[I2C:PM] +++IOCTL_POWER_QUERY\r\n"));
            
            if (pBufOut && dwLenOut >= sizeof(CEDEVICE_POWER_STATE)) 
            {
                __try 
                {
                    CEDEVICE_POWER_STATE PowerState = *(PCEDEVICE_POWER_STATE)pBufOut;
                    if(!VALID_DX(PowerState)) bResult = FALSE;
                }
                __except(pPublicCtxt->pI2CCtrlCtxt->I2CProcessException(GetExceptionInformation())) 
                {
                    ERRMSG((L"[I2C:PM] IOCTL_POWER_QUERY EXCEPTION\r\n"));
                    bResult = FALSE;
                }
            }

            DBGMSG(IIC_PM, (L"[I2C:PM] ---IOCTL_POWER_QUERY\r\n"));
            break;
            
        case IOCTL_POWER_SET:
       		DBGMSG(IIC_PM, (L"[I2C:PM] +++IOCTL_POWER_SET\r\n"));
                RETAILMSG(1, (L"[I2C:PM] +++IOCTL_POWER_SET\r\n"));
                /*
    		if (pBufOut && dwLenOut >= sizeof(CEDEVICE_POWER_STATE))
    		{
    			__try
    			{
    				CEDEVICE_POWER_STATE PowerState = *(PCEDEVICE_POWER_STATE)pBufOut;
    				DBGMSG(IIC_PM, (L"[I2C:PM] IOCTL_POWER_SET : D%d \r\n", PowerState));
    				
    				if (VALID_DX(PowerState))
    				{
    				    *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
    				    *(PCEDEVICE_POWER_STATE) pBufOut = I2C_SetPowerState(pPublicCtxt, PowerState);
    				}
    			}
    			__except(pPublicCtxt->pI2CCtrlCtxt->I2CProcessException(GetExceptionInformation()))
    			{
    				ERRMSG((L"[I2C:PM] IOCTL_POWER_SET EXCEPTION\r\n"));
    				bResult = FALSE;
    			}
    		}

            DBGMSG(IIC_PM, (L"[I2C:PM] ---IOCTL_POWER_SET\r\n"));*/
            break;
  
        case IOCTL_POWER_GET:
    		DBGMSG(IIC_PM, (L"[I2C:PM] +++IOCTL_POWER_GET\r\n"));

    		if (pBufOut != NULL && dwLenOut >= sizeof(CEDEVICE_POWER_STATE))
    		{
    			__try
    			{
    				*(PCEDEVICE_POWER_STATE)pBufOut = pPublicCtxt->pI2CCtrlCtxt->GetPowerState();
    				DBGMSG(IIC_PM, (L"[I2C:PM] IOCTL_POWER_GET : D%d \r\n", *pBufOut));

    			}
    			__except(pPublicCtxt->pI2CCtrlCtxt->I2CProcessException(GetExceptionInformation()))
    			{
    				ERRMSG((L"[I2C:PM] IOCTL_POWER_GET EXCEPTION\r\n"));
    		        			bResult = FALSE;
    			}
    		}
                
                DBGMSG(IIC_PM, (L"[I2C:PM] ---IOCTL_POWER_GET\r\n"));
            break;

		default:
			ERRMSG((L"[I2C:E] UNKNOWN IOCTL(%d)\r\n", dwCode));
			bResult = FALSE;
			break;
	}

	if(bStartTransfer)
	{

		pPublicCtxt->pI2CCtrlCtxt->Lock();
		bResult = pPublicCtxt->pI2CCtrlCtxt->StartTransfer(pI2CObj);
		pPublicCtxt->pI2CCtrlCtxt->Unlock();

		if(btACK == I2C_NAK)
		{
			bResult = FALSE;
			ERRMSG((L"[I2C:E] Slave didn't send ACK\r\n"));
		}

		if(!bReady)
		{
			bResult = FALSE;
			ERRMSG((L"[I2C:E] Slave is driving SCL line\r\n"));		
		}
	}

	I2CObj.Unlock();

#ifdef TEST_MODE	
	if(bResult) DBGMSG(IIC_INFO,(L"[I2C:I] IOCTL RESULT : SUCCESS\r\n"));	
	else 		DBGMSG(IIC_INFO,(L"[I2C:I] IOCTL RESULT : FAIL\r\n"));	
#endif

	return bResult;
}
 
