/*
 * Project Name Codec Memory Management DRIVER IN WINCE
 * Copyright  2007 Samsung Electronics Co, Ltd. All Rights Reserved.
 *
 * This software is the confidential and proprietary information
 * of Samsung Electronics  ("Confidential Information").
 * you shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Samsung Electronics
 *
 * This file implements JPEG driver.
 *
 * @name  Codec Memory Management DRIVER MODULE Module (CMMDriver.c)
 * @author Jiyoung Shin (idon.shin@samsung.com)
 * @date 23-04-08
 */


#include "CMMDriver.h"
#include "CMMMisc.h"
#include <pmplatform.h>
#include <bsp_cfg.h>
#include <windows.h>
#include <ceddk.h>
#include <nkintr.h>
#include <image_cfg.h>


BOOL            instanceNo[MAX_INSTANCE_NUM];
ALLOC_MEM_T    *AllocMemHead;
ALLOC_MEM_T    *AllocMemTail;
FREE_MEM_T        *FreeMemHead;
FREE_MEM_T        *FreeMemTail;
UINT8            *VirAddrDram_FrameBuffer;
UINT8            *VirAddrDram_0;
UINT8            *VirAddrDram_1;


/*----------------------------------------------------------------------------
*Function: CMM_Init

*Parameters:         dwContext        :
*Return Value:        True/False
*Implementation Notes: Initialize JPEG Hardware
-----------------------------------------------------------------------------*/
DWORD
CMM_Init(
    DWORD dwContext
    )
{

    //CODEC_MEM_CTX *CodecMem;
    HANDLE            h_Mutex;
    DWORD            ret;
    FREE_MEM_T *    free_node;
    FREE_MEM_T *    alloc_node;

    printD("\n[CMM_Init]\n");


    // Mutex initialization
    h_Mutex = CreateCMMmutex();
    if (h_Mutex == NULL)
    {
        RETAILMSG(1, (TEXT("[CMM_Init] CMM Mutex Initialize error : %d \r\n"),GetLastError()));
        return FALSE;
    }

    ret = LockCMMMutex();
    if(!ret){
        RETAILMSG(1, (TEXT("[CMM_Init] CMM Mutex Lock Fail\r\n")));
        return FALSE;
    }

#if ((CODEC_MEM_START_DRAM_0%65536 == 0) && (CODEC_MEM_START_DRAM_1%65536 == 0))
    VirAddrDram_0 = DrvLib_MapIoSpace(CODEC_MEM_START_DRAM_0, CODEC_MEM_SIZE_DRAM_0, FALSE);
    if (VirAddrDram_0 == NULL)
    {
		RETAILMSG(1, (TEXT("[CMM_Init] CODEC_MEM_START_DRAM_0 Allocation Failed[PA:0x%08x]\r\n"), CODEC_MEM_START_DRAM_0));
        return FALSE;
    }

    VirAddrDram_1 = DrvLib_MapIoSpace(CODEC_MEM_START_DRAM_1, CODEC_MEM_SIZE_DRAM_1, FALSE);
    if (VirAddrDram_1 == NULL)
    {
		RETAILMSG(1, (TEXT("[CMM_Init] CODEC_MEM_START_DRAM_1 Allocation Failed[PA:0x%08x]\r\n"), CODEC_MEM_START_DRAM_1));
        return FALSE;
    }
#else
#error CODEC_MEM_START_DRAM_0 and CODEC_MEM_START_DRAM_1 must be aligned at 64KB
#endif

    printf("[CMM_Init] VirAddrDram_0 : 0x%08x VirAddrDram_1 : 0x%08x\n", VirAddrDram_0, VirAddrDram_1);

    // init alloc list, if(AllocMemHead == AllocMemTail) then, the list is NULL
    alloc_node = (FREE_MEM_T *)malloc(sizeof(FREE_MEM_T));
    memset(alloc_node, 0x00, sizeof(FREE_MEM_T));
    alloc_node->next = alloc_node;
    alloc_node->prev = alloc_node;
    AllocMemHead = alloc_node;
    AllocMemTail = AllocMemHead;

    // init free list, if(FreeMemHead == FreeMemTail) then, the list is NULL
    free_node = (FREE_MEM_T *)malloc(sizeof(FREE_MEM_T));
    memset(free_node, 0x00, sizeof(FREE_MEM_T));
    free_node->next = free_node;
    free_node->prev = free_node;
    FreeMemHead = free_node;
    FreeMemTail = FreeMemHead;

    free_node = (FREE_MEM_T *)malloc(sizeof(FREE_MEM_T));
    memset(free_node, 0x00, sizeof(FREE_MEM_T));
    free_node->startAddr = CODEC_MEM_START_DRAM_0;
    free_node->size = CODEC_MEM_SIZE_DRAM_0;
    free_node->dramLocation = DRAM_0;
    InsertNodeToFreeList(free_node, -1);

    free_node = (FREE_MEM_T *)malloc(sizeof(FREE_MEM_T));
    memset(free_node, 0x00, sizeof(FREE_MEM_T));
    free_node->startAddr = CODEC_MEM_START_DRAM_1;
    free_node->size = CODEC_MEM_SIZE_DRAM_1;
    free_node->dramLocation = DRAM_1;
    InsertNodeToFreeList(free_node, -1);

    UnlockCMMMutex();
    return TRUE;
}


/*----------------------------------------------------------------------------
*Function: CMM_DeInit

*Parameters:         InitHandle        :
*Return Value:        True/False
*Implementation Notes: Deinitialize JPEG Hardware
-----------------------------------------------------------------------------*/
BOOL
CMM_Deinit(
    DWORD InitHandle
    )
{
    CODEC_MEM_CTX *CodecMem;

    printD("[CMM_DeInit] CMM_Deinit\n");
    CodecMem = (CODEC_MEM_CTX *)InitHandle;

    if(!CodecMem){
        RETAILMSG(1, (TEXT("[CMM_DeInit] CMM Invalid Input Handle\r\n")));
        return FALSE;
    }

    DeleteCMMMutex();
    return TRUE;
}


/*----------------------------------------------------------------------------
*Function: CMM_Open

*Parameters:         InitHandle        :Handle to JPEG  context
                    dwAccess        :
                    dwShareMode        :File share mode of JPEG
*Return Value:        This function returns a handle that identifies the
                    open context of JPEG  to the calling application.
*Implementation Notes: Opens JPEG CODEC device for reading, writing, or both
-----------------------------------------------------------------------------*/
DWORD
CMM_Open(
    DWORD InitHandle,
    DWORD dwAccess,
    DWORD dwShareMode
    )
{
    CODEC_MEM_CTX *CodecMem;
    DWORD    ret;
    int    inst_no;


    ret = LockCMMMutex();
    if(!ret){
        RETAILMSG(1, (TEXT("[CMM_Open] CMM Mutex Lock Fail\r\n")));
        return FALSE;
    }

    // check the number of instance
    if((inst_no = GetInstanceNo()) < 0){
        RETAILMSG(1, (TEXT("[CMM_Open] Instance Number error-too many instance\r\n")));
        UnlockCMMMutex();
        return FALSE;
    }

    CodecMem = (CODEC_MEM_CTX *)malloc(sizeof(CODEC_MEM_CTX));
    if(CodecMem == NULL){
        RETAILMSG(1, (TEXT("[CMM_Init] CodecMem allocatopn failed\r\n")));
        UnlockCMMMutex();
        return FALSE;
    }

    memset(CodecMem, 0x00, sizeof(CODEC_MEM_CTX));

    CodecMem->inst_no = inst_no;
    printD("\n*****************************\n[CMM_Open] instanceNo : %d\n*****************************\n", CodecMem->inst_no);
    PrintList();
    UnlockCMMMutex();
    return (DWORD)CodecMem;
}


/*----------------------------------------------------------------------------
*Function: CMM_Close

*Parameters:         OpenHandle        :
*Return Value:        True/False
*Implementation Notes: This function closes the device context identified by
                        OpenHandle
-----------------------------------------------------------------------------*/
BOOL
CMM_Close(
    DWORD OpenHandle
    )
{
    CODEC_MEM_CTX *CodecMem;
    DWORD    ret;
    ALLOC_MEM_T *node, *tmp_node;
    int        count=0;

    ret = LockCMMMutex();
    if(!ret){
        RETAILMSG(1, (TEXT("[CMM_Close] CMM Mutex Lock Fail\r\n")));
        return FALSE;
    }

    CodecMem = (CODEC_MEM_CTX *)OpenHandle;

    if(!CodecMem){
        RETAILMSG(1, (TEXT("[CMM_Close] CMM Invalid Input Handle\r\n")));
        UnlockCMMMutex();
        return FALSE;
    }

    printD("[CMM_Close] CodecMem->inst_no : %d CodecMem->callerProcess : 0x%x\n", CodecMem->inst_no, CodecMem->callerProcess);

    // release u_addr and v_addr accoring to inst_no
    for(node = AllocMemHead; node != AllocMemTail; node = node->next){
        if(node->inst_no == CodecMem->inst_no){
            tmp_node = node;
            node = node->prev;
            ReleaseAllocMem(tmp_node, CodecMem);
        }
    }


    printD("[%d][CMM Close] MergeFragmentation\n", CodecMem->inst_no);
    MergeFragmentation(CodecMem->inst_no);

    ReturnInstanceNo(CodecMem->inst_no);

    free(CodecMem);
    UnlockCMMMutex();


    return TRUE;
}


/*----------------------------------------------------------------------------
*Function: CMM_IOControl

*Parameters:         OpenHandle        :
                    dwIoControlCode    :
*Return Value:        True/False
*Implementation Notes: JPEG_IOControl sends commands to initiate different
*                       operations like Init,Decode and Deinit.The test
*                       application uses the DeviceIOControl function to
*                       specify an operation to perform
-----------------------------------------------------------------------------*/
BOOL
CMM_IOControl(
    DWORD OpenHandle,
    DWORD dwIoControlCode,
    PBYTE pInBuf,
    DWORD nInBufSize,
    PBYTE pOutBuf,
    DWORD nOutBufSize,
    PDWORD pBytesReturned
    )
{
    CODEC_MEM_CTX         *CodecMem;
    BOOL                result = TRUE;
    DWORD                ret;
    UINT8                *u_addr;
    ALLOC_MEM_T        *node;
    CMM_ALLOC_PRAM_T     *allocParam;

    ret = LockCMMMutex();
    if(!ret){
        RETAILMSG(1, (TEXT("[CMM_IOControl] CMM Mutex Lock Fail\r\n")));
        return FALSE;
    }

    CodecMem = (CODEC_MEM_CTX *)OpenHandle;

    if(!CodecMem){
        RETAILMSG(1, (TEXT("[CMM_IOControl] CMM Invalid Input Handle\r\n")));
        UnlockCMMMutex();
        return FALSE;
    }



    switch ( dwIoControlCode ) {

    case IOCTL_CODEC_MEM_ALLOC:
            printD("\n[%d][CMM_IOControl] IOCTL_CODEC_MEM_ALLOC\n", CodecMem->inst_no);
            allocParam = (CMM_ALLOC_PRAM_T *)pInBuf;
            printD("[CMM_IOControl] buffSize : 0x%08x\n", allocParam->size);

            allocParam->size = Align(allocParam->size , 64*1024);   // align to 64KB

            if((node = GetCodecVirAddr(CodecMem->inst_no, allocParam, CMM_TYPE_CODEC)) == NULL){
                result = FALSE;
                break;
            }

        #if    (_WIN32_WCE >= 600)    
		{
			CodecMem->callerProcess = (HANDLE) GetDirectCallerProcessId();
			node->u_addr = (PBYTE) VirtualAllocEx(CodecMem->callerProcess, NULL, node->size, MEM_RESERVE ,PAGE_NOACCESS);       // HANDLE hSrcProc                                                        
			if (node->u_addr== FALSE)
			{
				RETAILMSG(1, (TEXT("DD:: error : %d \r\n"),GetLastError()));    
				RETAILMSG(1, (L"DD::CMM VirtualAllocEx(node->u_addr) returns FALSE.\n"));
				break;
			}        
			printD("[IOCTL_CODEC_MEM_ALLOC] node->u_addr : 0x%x\n", node->u_addr);
			printD("[IOCTL_CODEC_MEM_ALLOC] node->cached_p_addr : 0x%x\n", node->cached_p_addr);
			
			result = VirtualCopyEx(CodecMem->callerProcess,         // HANDLE hDstProc
								node->u_addr,
								(HANDLE) GetCurrentProcessId(),     // HANDLE hSrcProc
								(PVOID)(node->cached_p_addr >> 8),
								node->size,
								PAGE_PHYSICAL |PAGE_READWRITE |PAGE_NOCACHE );        
			
			if (result== FALSE)
			{
				RETAILMSG(1, (TEXT("DD:: error : %d \r\n"),GetLastError()));    
				RETAILMSG(1, (L"DD::CMM VirtualCopyEx(node->u_addr) returns FALSE.\n"));
				break;
		   }
		   printf("[IOCTL_CODEC_MEM_ALLOC] u_addr:0x%x  CallerProcessID : 0x%x\n",  node->u_addr, CodecMem->callerProcess);
		   *((UINT *)pOutBuf) = (UINT) node->u_addr;
		}
        #else
            *((UINT *)pOutBuf) = (UINT) node->v_addr;
        #endif
            break;
        
    case IOCTL_DISPLAY_MEM_ALLOC:
            printD("\n[%d][CMM_IOControl] IOCTL_CODEC_MEM_ALLOC\n", CodecMem->inst_no);
            allocParam = (CMM_ALLOC_PRAM_T *)pInBuf;
            printD("[CMM_IOControl] buffSize : 0x%08x\n", allocParam->size);

            allocParam->size = Align(allocParam->size , 64*1024);   // align to 64KB

        #if(S5PV210_EVT==0) // due to the restriction of 24-bit boundary in FIMD buffer
            if((node = GetCodecVirAddr(CodecMem->inst_no, allocParam, CMM_TYPE_DISPLAY)) == NULL){
                result = FALSE;
                break;
            }
        #else
            if((node = GetCodecVirAddr(CodecMem->inst_no, allocParam, CMM_TYPE_CODEC)) == NULL){
                result = FALSE;
                break;
            }
        #endif

        #if    (_WIN32_WCE >= 600)    
		{
			CodecMem->callerProcess = (HANDLE) GetDirectCallerProcessId();
			node->u_addr = (PBYTE) VirtualAllocEx(CodecMem->callerProcess, NULL, node->size, MEM_RESERVE ,PAGE_NOACCESS);       // HANDLE hSrcProc                                                        
			if (node->u_addr== FALSE)
			{
				RETAILMSG(1, (TEXT("DD:: error : %d \r\n"),GetLastError()));    
				RETAILMSG(1, (L"DD::CMM VirtualAllocEx(node->u_addr) returns FALSE.\n"));
				break;
			}        
			printD("[IOCTL_CODEC_MEM_ALLOC] node->u_addr : 0x%x\n", node->u_addr);
			printD("[IOCTL_CODEC_MEM_ALLOC] node->cached_p_addr : 0x%x\n", node->cached_p_addr);
			
			result = VirtualCopyEx(CodecMem->callerProcess,         // HANDLE hDstProc
								node->u_addr,
								(HANDLE) GetCurrentProcessId(),     // HANDLE hSrcProc
								(PVOID)(node->cached_p_addr >> 8),
								node->size,
								PAGE_PHYSICAL |PAGE_READWRITE |PAGE_NOCACHE );        
			
			if (result== FALSE)
			{
				RETAILMSG(1, (TEXT("DD:: error : %d \r\n"),GetLastError()));    
				RETAILMSG(1, (L"DD::CMM VirtualCopyEx(node->u_addr) returns FALSE.\n"));
				break;
		   }
		   printf("[IOCTL_CODEC_MEM_ALLOC] u_addr:0x%x  CallerProcessID : 0x%x\n",  node->u_addr, CodecMem->callerProcess);
		   *((UINT *)pOutBuf) = (UINT) node->u_addr;
		}
        #else
            *((UINT *)pOutBuf) = (UINT) node->v_addr;
        #endif
            break;
        
    case IOCTL_CODEC_MEM_FREE:
    case IOCTL_DISPLAY_MEM_FREE:
            printD("\n[%d][CMM_IOControl] IOCTL_CODEC_MEM_FREE or IOCTL_DISPLAY_MEM_FREE\n", CodecMem->inst_no);
            u_addr = (UINT8 *)pInBuf;
            printD("[CMM_IOControl] free adder : 0x%x \n", u_addr);

            for(node = AllocMemHead; node != AllocMemTail; node = node->next)
        #if    (_WIN32_WCE >= 600)
                if(node->u_addr == u_addr)
        #else
                if(node->v_addr == u_addr)
        #endif
                    break;

            if(node  == AllocMemTail){
                RETAILMSG(1, (TEXT("[CMM_IOControl] invalid virtual address(0x%x)\r\n"), u_addr));
                result = FALSE;
                break;
            }
            // free alloc node
            ReleaseAllocMem(node, CodecMem);
            MergeFragmentation(CodecMem->inst_no);
            break;

    case IOCTL_CODEC_CACHE_INVALIDATE:
            printD("\n[CMM_IOControl] IOCTL_CODEC_CACHE_INVALIDATE\n");
            u_addr = (UINT8 *)pInBuf;
            printD("[CMM_IOControl] flush adder : 0x%x \n", u_addr);

            for(node = AllocMemHead; node != AllocMemTail; node = node->next)
        #if    (_WIN32_WCE >= 600)
                if(node->u_addr == u_addr)
        #else
                if(node->v_addr == u_addr)
        #endif
                    break;

            if(node  == AllocMemTail){
                RETAILMSG(1, (TEXT("[%d][CMM_IOControl] invalid virtual address(0x%x)\r\n"), CodecMem->inst_no, u_addr));
                result = FALSE;
                break;
            }

            InvalidateCacheRange((PBYTE) node->v_addr,
                                    (PBYTE) node->v_addr + node->size);
            break;

    case IOCTL_CODEC_CACHE_CLEAN:
            printD("\n[CMM_IOControl] IOCTL_CODEC_CACHE_CLEAN\n");
            u_addr = (UINT8 *)pInBuf;
            printD("[CMM_IOControl] flush adder : 0x%x \n", u_addr);

            for(node = AllocMemHead; node != AllocMemTail; node = node->next)
        #if    (_WIN32_WCE >= 600)
                if(node->u_addr == u_addr)
        #else
                if(node->v_addr == u_addr)
        #endif
                    break;

            if(node  == AllocMemTail){
                RETAILMSG(1, (TEXT("[%d][CMM_IOControl] invalid virtual address(0x%x)\r\n"), CodecMem->inst_no, u_addr));
                result = FALSE;
                break;
            }

            CleanCacheRange((PBYTE) node->v_addr,
                                    (PBYTE) node->v_addr + node->size);
            break;


    // IOCTL_CODEC_CACHE_FLUSH is same as IOCTL_CODEC_CACHE_CLEAN_INVALIDATE.
    // This is remained for backward capability
    case IOCTL_CODEC_CACHE_FLUSH:
    case IOCTL_CODEC_CACHE_CLEAN_INVALIDATE:
            printD("\n[CMM_IOControl] IOCTL_CODEC_CACHE_CLEAN_INVALIDATE\n");
            u_addr = (UINT8 *)pInBuf;
            printD("[CMM_IOControl] flush adder : 0x%x \n", u_addr);

            for(node = AllocMemHead; node != AllocMemTail; node = node->next)
        #if    (_WIN32_WCE >= 600)
                if(node->u_addr == u_addr)
        #else
                if(node->v_addr == u_addr)
        #endif
                    break;

            if(node  == AllocMemTail){
                RETAILMSG(1, (TEXT("[%d][CMM_IOControl] invalid virtual address(0x%x)\r\n"), CodecMem->inst_no, u_addr));
                result = FALSE;
                break;
            }

            CleanInvalidateCacheRange((PBYTE) node->v_addr,
                                    (PBYTE) node->v_addr + node->size);
            break;


    case IOCTL_CODEC_GET_PHY_ADDR:
            u_addr  = (UINT8 *)pInBuf;
            //printD("\n[CMM_IOControl] IOCTL_CODEC_GET_PHY_ADDR(0x%x)\n", u_addr);
            for(node = AllocMemHead; node != AllocMemTail; node = node->next)
        #if    (_WIN32_WCE >= 600)
                if(node->u_addr == u_addr)
        #else
                if(node->v_addr == u_addr)
        #endif
                    break;

            if(node  == AllocMemTail){
                RETAILMSG(1, (TEXT("[CMM_IOControl] invalid virtual address(0x%x)\r\n"), u_addr));
                result = FALSE;
                break;
            }

            *((UINT *)pOutBuf) = (UINT) node->cached_p_addr;

            //printD("\n[CMM_IOControl] p_addr (0x%x)\n", node->cached_p_addr);
            break;


    default : RETAILMSG(1, (TEXT("[CMM_IOControl] CMM Invalid IOControl\r\n")));
    }


    UnlockCMMMutex();
    return result;
}

/*----------------------------------------------------------------------------
*Function: CMM_Write

*Parameters:         dwContext        :
*Return Value:        True/False
*Implementation Notes: Initialize JPEG Hardware
-----------------------------------------------------------------------------*/
DWORD CMM_Write(
    DWORD OpenHandle,
    LPCVOID pBuffer,
    DWORD dwNumBytes
    )
{
    printD("DD::CMM_Write \n");
    return 1;
}

/*----------------------------------------------------------------------------
*Function: CMM_PowerUp

*Parameters:         dwContext        :
*Return Value:        True/False
*Implementation Notes: Initialize JPEG Hardware
-----------------------------------------------------------------------------*/
void CMM_PowerUp(
    DWORD InitHandle
    )
{
    printD("DD::CMM_PowerUp \n");
}

/*----------------------------------------------------------------------------
*Function: CMM_PowerDown

*Parameters:         dwContext        :
*Return Value:        True/False
*Implementation Notes: Initialize JPEG Hardware
-----------------------------------------------------------------------------*/
void CMM_PowerDown(
    DWORD InitHandle
    )
{
    printD("DD::CMM_PowerDown(instanceNo : %d \n", instanceNo);
}


/*----------------------------------------------------------------------------
*Function: CMM_DllMain

*Parameters:         DllInstance        :
                    Reason            :
                    Reserved        :
*Return Value:        True/False
*Implementation Notes: Entry point for CMM.dll
-----------------------------------------------------------------------------*/
BOOL WINAPI
CMM_DllMain(HINSTANCE DllInstance, DWORD Reason, LPVOID Reserved)
{
    switch(Reason) {
        case DLL_PROCESS_ATTACH:
            //DEBUGREGISTER(DllInstance);
            break;
    }
    return TRUE;
}

// insert node ahead of AllocMemHead
static void InsertNodeToAllocList(ALLOC_MEM_T *node, UINT8 inst_no)
{
    printD("== [%d]InsertNodeToAllocList(p_addr : 0x%08x size:0x%08x cacheflag : %d)\n\n", inst_no, node->cached_p_addr, node->size, node->cacheFlag);
    node->next = AllocMemHead;
    node->prev = AllocMemHead->prev;
    AllocMemHead->prev->next = node;
    AllocMemHead->prev = node;
    AllocMemHead = node;
    
    PrintList();

}

// insert node ahead of FreeMemHead
static void InsertNodeToFreeList(FREE_MEM_T *node,  UINT8 inst_no)
{
    printD("== [%d]InsertNodeToFreeList(startAddr : 0x%08x size:0x%08x)\n\n", inst_no, node->startAddr, node->size);
    node->next = FreeMemHead;
    node->prev = FreeMemHead->prev;
    FreeMemHead->prev->next = node;
    FreeMemHead->prev = node;
    FreeMemHead = node;

    PrintList();
}

static void DeleteNodeFromAllocList(ALLOC_MEM_T *node, UINT8 inst_no)
{
    printD("== [%d]DeleteNodeFromAllocList(p_addr : 0x%08x size:0x%08x cacheflag : %d)\n\n", inst_no, node->cached_p_addr, node->size, node->cacheFlag);
    if(node == AllocMemTail){
        RETAILMSG(1, (TEXT("[CMM] DeleteNodeFromAllocList :: InValid node\n")));
        return;
    }

    if(node == AllocMemHead)
        AllocMemHead = node->next;

    node->prev->next = node->next;
    node->next->prev = node->prev;

    free(node);

    PrintList();
}

static void DeleteNodeFromFreeList( FREE_MEM_T *node, UINT8 inst_no)
{
    printD("== [%d]DeleteNodeFromFreeList(startAddr : 0x%08x size:0x%08x)\n\n", inst_no, node->startAddr, node->size);
    if(node == FreeMemTail){
        RETAILMSG(1, (TEXT("[CMM] DeleteNodeFromFreeList :: InValid node\n")));
        return;
    }

    if(node == FreeMemHead)
        FreeMemHead = node->next;

    node->prev->next = node->next;
    node->next->prev = node->prev;

    free(node);

    PrintList();
}


static void ReleaseAllocMem(ALLOC_MEM_T *node, CODEC_MEM_CTX *CodecMem)
{
    FREE_MEM_T *free_node;
    BOOL        r;
    
#if    (_WIN32_WCE >= 600)    
    {
        printD("decommit CodecAddr\n");
        r = VirtualFreeEx(CodecMem->callerProcess,    // HANDLE hProcess
            node->u_addr,
            0,
            MEM_RELEASE);
        if (r == FALSE)
        {
            RETAILMSG(1, (L"[%d][CMM_Close] CMM VirtualFreeEx returns FALSE.(cacheFlag : %d callerprocess:%ld)\n", 
            CodecMem->inst_no, node->cacheFlag, CodecMem->callerProcess));
        }
    }
#endif

    FreeCodecBuff(node);

    if(1/*node->cacheFlag*/){
        free_node = (FREE_MEM_T    *)malloc(sizeof(FREE_MEM_T));
        free_node->startAddr = node->cached_p_addr;
        free_node->size = node->size;
        free_node->dramLocation = node->dramLocation;
        InsertNodeToFreeList(free_node, CodecMem->inst_no);
    }

    // Delete from AllocMem list
    DeleteNodeFromAllocList(node, CodecMem->inst_no);
}



// Remove Fragmentation in FreeMemList
static void MergeFragmentation(UINT8 inst_no)
{
    FREE_MEM_T *node1, *node2;

    node1 = FreeMemHead;

    while(node1 != FreeMemTail){
        node2 = FreeMemHead;
        while(node2 != FreeMemTail){
            if((node1->startAddr + node1->size == node2->startAddr) && (node1->dramLocation == node2->dramLocation)){
                node1->size += node2->size;
                printD("find merge area !! ( node1->startAddr + node1->size == node2->startAddr)\n");
                DeleteNodeFromFreeList(node2, inst_no);
                break;
            }
            else if((node1->startAddr == node2->startAddr + node2->size) && (node1->dramLocation == node2->dramLocation)){
                printD("find merge area !! ( node1->startAddr == node2->startAddr + node2->size)\n");
                node1->startAddr = node2->startAddr;
                node1->size += node2->size;
                DeleteNodeFromFreeList(node2, inst_no);
                break;
            }
            node2 = node2->next;
        }
        node1 = node1->next;
    }
}
static DWORD GetMemArea(UINT32 allocSize, CMM_DRAM_LOCATION loc, UINT8 inst_no, CMM_MEM_TYPE mem_type)
{
    FREE_MEM_T      *node, *match_node = NULL, *free_node = NULL;
    DWORD           allocAddr = 0, endAddr = 0, boundAddr = 0;
    int             i = 0, NeedDivdeNode = 0, secondNodeSize = 0;

    printD("request Size : 0x%08x\n", allocSize);

    if(FreeMemHead == FreeMemTail){
        printf("all memory is gone\n");
        return(allocAddr);
    }

    if(mem_type == CMM_TYPE_CODEC){
        printD("CMM_TYPE_CODEC\n");
        // find best chunk of memory
        for(node = FreeMemHead; node != FreeMemTail; node = node->next){

            if(match_node != NULL){
                if((node->size >= allocSize) && (node->size < match_node->size) && (node->dramLocation== loc))
                    match_node = node;
            }
            else{
                if((node->size >= allocSize) && (node->dramLocation == loc))
                    match_node = node;
            }

        }

        // rearange FreeMemArea
        if(match_node != NULL){
            printD("match : startAddr(0x%08x) size(0x%08x) (loc : %d)\n", match_node->startAddr, match_node->size, match_node->dramLocation);

            allocAddr = match_node->startAddr;
            match_node->startAddr += allocSize;
            match_node->size -= allocSize;

            if(match_node->size == 0)          // delete match_node.
                 DeleteNodeFromFreeList(match_node, inst_no);

            return(allocAddr);
        }else printf("there is no suitable chunk\n");
    }
    else{
        printD("CMM_TYPE_DISPLAY\n");

        for(node = FreeMemHead; node != FreeMemTail; node = node->next){
            if(match_node != NULL){ // find best chunk
                if((node->size >= allocSize) && (node->size < match_node->size) && (node->dramLocation== loc)){
                    boundAddr = (node->startAddr + 0xffffff) & 0xff000000;
                    endAddr = node->startAddr + node->size - 1;
                    printD("[find best chunk] endAddr : 0x%08x boundAddr : 0x%08x end - bound : 0x%08x\n", boundAddr, endAddr, endAddr - boundAddr);
                    if((endAddr&0xff000000) == (node->startAddr&0xff000000)){ 
                        match_node = node;
                        NeedDivdeNode = FALSE;
                        printD("[find best chunk] find node !!!\n");
                    }
                    else if((endAddr - boundAddr) >= allocSize){ 
                        match_node = node;
                        NeedDivdeNode = TRUE;
                        printD("[find best chunk] find node !!! - need divide node\n");
                    }
                 }
            }
            else{ // find chunk
                if((node->size >= allocSize) && (node->dramLocation == loc)){
                    boundAddr = (node->startAddr + 0xffffff) & 0xff000000;
                    endAddr = node->startAddr + node->size - 1;
                    printD("[find chunk]endAddr : 0x%08x boundAddr : 0x%08x end - bound : 0x%08x\n", boundAddr, endAddr, endAddr - boundAddr);
                    if((endAddr&0xff000000) == (node->startAddr&0xff000000)){ 
                        match_node = node;
                        NeedDivdeNode = FALSE;
                        printD("[find chunk] find node !!!\n");
                    }
                    else if((endAddr - boundAddr) >= allocSize){ 
                        match_node = node;
                        NeedDivdeNode = TRUE;
                        printD("[find chunk] find node !!! - need divide node\n");
                    }
                }
             }
        }

        // rearange FreeMemArea
        if(match_node != NULL){
            printD("match : startAddr(0x%08x) size(0x%08x) (loc : %d)\n", match_node->startAddr, match_node->size, match_node->dramLocation);

            if(NeedDivdeNode){
                boundAddr = (match_node->startAddr + 0x1000000) & 0xff000000;
                secondNodeSize = match_node->size - allocSize - (boundAddr - match_node->startAddr);

                if(secondNodeSize != 0){
                    free_node = (FREE_MEM_T    *)malloc(sizeof(FREE_MEM_T));
                    free_node->startAddr = boundAddr + allocSize;
                    free_node->size = match_node->size - allocSize - (boundAddr - match_node->startAddr);
                    free_node->dramLocation = node->dramLocation;
                    InsertNodeToFreeList(free_node, inst_no);
                }
       
                allocAddr = boundAddr;
                match_node->size = boundAddr - match_node->startAddr;

                return(allocAddr);
            }
            else{
                allocAddr = match_node->startAddr;
                match_node->startAddr += allocSize;
                match_node->size -= allocSize;

                if(match_node->size == 0)          // delete match_node.
                     DeleteNodeFromFreeList(match_node, inst_no);

                return(allocAddr);
            }
        }
        else RETAILMSG(1, (L"[CMM:ERR]there is no suitable chunk\n\r"));
      }

    return(allocAddr);
}


static ALLOC_MEM_T * GetCodecVirAddr(UINT8 inst_no, CMM_ALLOC_PRAM_T *in_param, CMM_MEM_TYPE mem_type)
{

    DWORD                    p_startAddr;
    ALLOC_MEM_T             *p_allocMem;


    //
    // if user request cachable area, allocate from reserved area
    // if user request uncachable area, allocate dynamically
    //

    printD("GetCodecVirAddr \n");

    p_startAddr = GetMemArea((UINT32)in_param->size, in_param->dramLocation, inst_no, mem_type);

    if(!p_startAddr){
        RETAILMSG(1, (L"[CMM:ERR] There is no more memory\n\r"));
        return NULL;
    }

    p_allocMem = (ALLOC_MEM_T *)malloc(sizeof(ALLOC_MEM_T));
    if(!p_allocMem)
    {
        return NULL;
    }
    memset(p_allocMem, 0x00, sizeof(ALLOC_MEM_T));


    p_allocMem->cached_p_addr = p_startAddr;
    
    switch(in_param->dramLocation){
        case DRAM_0           : p_allocMem->v_addr =  VirAddrDram_0 + (p_allocMem->cached_p_addr - CODEC_MEM_START_DRAM_0);
                                break;
        case DRAM_1           : p_allocMem->v_addr =  VirAddrDram_1 + (p_allocMem->cached_p_addr - CODEC_MEM_START_DRAM_1);
                                break;
        default               : RETAILMSG(1, (L"[CMM:ERR]unknown dram location(%d)\n\r", in_param->dramLocation));
    }
        

    if (p_allocMem->v_addr == NULL)
    {
        RETAILMSG(1, (L"[CMM:ERR] Phy2Vir_AddrMapping() : Mapping Failed [PA:0x%08x]\n\r", p_allocMem->cached_p_addr));
        free(p_allocMem);
        return NULL;
    }
    p_allocMem->size = (UINT32)in_param->size;
    p_allocMem->inst_no = inst_no;
    p_allocMem->cacheFlag = in_param->cacheFlag;
    p_allocMem->dramLocation = in_param->dramLocation;
    printf("[CMM] v_addr : 0x%x p_addr : 0x%x size : 0x%08x loc : %d\n", p_allocMem->v_addr, p_allocMem->cached_p_addr, p_allocMem->size, p_allocMem->dramLocation);
    InsertNodeToAllocList(p_allocMem, inst_no);

    return(p_allocMem);
}

static void FreeCodecBuff(ALLOC_MEM_T *node)
{

    printD("FreeCodecBuff \n");

#if 0
    if(!node->cacheFlag){
        memset(&Adapter, 0, sizeof(DMA_ADAPTER_OBJECT));
            Adapter.InterfaceType = Internal;
            Adapter.ObjectSize = sizeof(DMA_ADAPTER_OBJECT);

        HalFreeCommonBuffer(&Adapter, 0, node->uncached_p_addr, (PVOID)node->v_addr, FALSE);
    }
#endif
}

static int GetInstanceNo()
{
    INT8    i;

    for(i = 0; i < MAX_INSTANCE_NUM; i++)
        if(instanceNo[i] == FALSE){
            instanceNo[i] = TRUE;
            return i;
        }

    if(i == MAX_INSTANCE_NUM)
        return -1;
}


static void ReturnInstanceNo(UINT8 inst_no)
{
    instanceNo[inst_no] = FALSE;

}


static void PrintList()
{
    ALLOC_MEM_T    *node1;
    FREE_MEM_T        *node2;
    int                 count = 0;
    DWORD            p_addr;

    for(node1 = AllocMemHead; node1 != AllocMemTail; node1 = node1->next){
        if(1/*node1->cacheFlag*/)
            p_addr = node1->cached_p_addr;
        else
            p_addr = (DWORD)node1->uncached_p_addr.LowPart;

        printD("     [AllocList][%d] inst_no : %d p_addr : 0x%08x v_addr:0x%08x size:0x%08x cacheflag : %d loc : %d\n",
            count++, node1->inst_no,  p_addr, node1->v_addr, node1->size, node1->cacheFlag, node1->dramLocation);

    }

    count = 0;
    for(node2 = FreeMemHead; node2 != FreeMemTail; node2 = node2->next){
            printD("     [FreeList][%d] startAddr : 0x%08x size:0x%08x loc : %d\n", count++, node2->startAddr , node2->size, node2->dramLocation);

    }


}

