/*******************************************************************************
**
**  gckVIDMEM_Free
**
**  Free an allocated video memory node.
**
**  INPUT:
**
**      gcuVIDMEM_NODE_PTR Node
**          Pointer to a gcuVIDMEM_NODE object.
**
**  OUTPUT:
**
**      Nothing.
*/
gceSTATUS
gckVIDMEM_Free(
    IN gcuVIDMEM_NODE_PTR Node
    )
{
    gckVIDMEM memory = gcvNULL;
    gcuVIDMEM_NODE_PTR node;
    gceSTATUS status;
    gctBOOL acquired = gcvFALSE;

    gcmkHEADER_ARG("Node=0x%x", Node);

    /* Verify the arguments. */
    if ((Node == gcvNULL)
    ||  (Node->VidMem.memory == gcvNULL)
    )
    {
        /* Invalid object. */
        gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
    }

    /**************************** Video Memory ********************************/

    if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
    {
        if (Node->VidMem.locked > 0)
        {
            gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_VIDMEM,
                           "Node 0x%x is locked (%d)",
                           Node, Node->VidMem.locked);

            /* Force unlock. */
            Node->VidMem.locked = 0;
        }

        /* Extract pointer to gckVIDMEM object owning the node. */
        memory = Node->VidMem.memory;

        /* Acquire the mutex. */
        gcmkONERROR(
            gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE));

        acquired = gcvTRUE;

#ifdef __QNXNTO__
        /* Reset handle to 0. */
        Node->VidMem.logical = gcvNULL;
        Node->VidMem.handle = 0;

        /* Don't try to a re-free an already freed node. */
        if ((Node->VidMem.nextFree == gcvNULL)
        &&  (Node->VidMem.prevFree == gcvNULL)
        )
#endif
        {
            /* Update the number of free bytes. */
            memory->freeBytes += Node->VidMem.bytes;

            /* Find the next free node. */
            for (node = Node->VidMem.next;
                 node->VidMem.nextFree == gcvNULL;
                 node = node->VidMem.next) ;

            /* Insert this node in the free list. */
            Node->VidMem.nextFree = node;
            Node->VidMem.prevFree = node->VidMem.prevFree;

            Node->VidMem.prevFree->VidMem.nextFree =
            node->VidMem.prevFree                  = Node;

            /* Is the next node a free node and not the sentinel? */
            if ((Node->VidMem.next == Node->VidMem.nextFree)
            &&  (Node->VidMem.next->VidMem.bytes != 0)
            )
            {
                /* Merge this node with the next node. */
                gcmkONERROR(_Merge(memory->os, node = Node));
                gcmkASSERT(node->VidMem.nextFree != node);
                gcmkASSERT(node->VidMem.prevFree != node);
            }

            /* Is the previous node a free node and not the sentinel? */
            if ((Node->VidMem.prev == Node->VidMem.prevFree)
            &&  (Node->VidMem.prev->VidMem.bytes != 0)
            )
            {
                /* Merge this node with the previous node. */
                gcmkONERROR(_Merge(memory->os, node = Node->VidMem.prev));
                gcmkASSERT(node->VidMem.nextFree != node);
                gcmkASSERT(node->VidMem.prevFree != node);
            }
        }

        /* Release the mutex. */
        gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex));

        /* Success. */
        gcmkFOOTER_NO();
        return gcvSTATUS_OK;
    }

    /*************************** Virtual Memory *******************************/

    /* Verify the gckKERNEL object pointer. */
    gcmkVERIFY_OBJECT(Node->Virtual.kernel, gcvOBJ_KERNEL);

#ifdef __QNXNTO__
    if (!Node->Virtual.unlockPending && (Node->Virtual.locked > 0))
#else
    if (!Node->Virtual.pending && (Node->Virtual.locked > 0))
#endif
    {
        gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_VIDMEM,
                       "gckVIDMEM_Free: Virtual node 0x%x is locked (%d)",
                       Node, Node->Virtual.locked);

        /* Force unlock. */
        Node->Virtual.locked = 0;
    }

#ifdef __QNXNTO__
    if (!Node->Virtual.freePending) { if (Node->Virtual.unlockPending)
#else
    if (Node->Virtual.pending)
#endif
    {
        gcmkASSERT(Node->Virtual.locked == 1);

        /* Schedule the node to be freed. */
        gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
                       "gckVIDMEM_Free: Scheduling node 0x%x to be freed later",
                       Node);

        /* Schedule the video memory to be freed again. */
        gcmkONERROR(gckEVENT_FreeVideoMemory(Node->Virtual.kernel->event,
                                             Node,
                                             gcvKERNEL_PIXEL));

#ifdef __QNXNTO__
        Node->Virtual.freePending = gcvTRUE; }
#endif

        /* Success. */
        gcmkFOOTER_NO();
        return gcvSTATUS_SKIP;
    }

    else
    {
        /* Free the virtual memory. */
        gcmkVERIFY_OK(gckOS_FreePagedMemory(Node->Virtual.kernel->os,
                                            Node->Virtual.physical,
                                            Node->Virtual.bytes));

        /* Destroy the gcuVIDMEM_NODE union. */
        gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node));
    }

    /* Success. */
    gcmkFOOTER_NO();
    return gcvSTATUS_OK;

OnError:
    if (acquired)
    {
        /* Release the mutex. */
        gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex));
    }

    /* Return the status. */
    gcmkFOOTER();
    return status;
}
int memory_engine_free(memory_engine_t *engine, int alignaddr)
{
	int res = 0;
#ifdef  SHM_GUARD_BYTES_ENABLE
	int flag  = 0;
#endif
	memory_node_t   *new_node, *node;

	shm_debug("memory_engine_free start. (0x%08X)\n", alignaddr);

	if ((engine == NULL) || (alignaddr == 0))
		return -EINVAL;

	down(&(engine->m_mutex));

	/* find alignaddr */
	node = memory_engine_lookup_shm_node(&(engine->m_shm_root), alignaddr);
	if (node == NULL) {
		printk("memory_engine_lookup_shm_node Error alignaddr[%x]\n",
								alignaddr);
		res = -EFAULT;
		goto err_exit;
	}

	memory_engine_delete_shm_node(&(engine->m_shm_root), node);

	/* if the node to be freed is a free one, there could be invalid operations*/
	if (node->m_next_free != NULL) {
		res = -EFAULT;
		goto err_exit;
	}

#ifdef SHM_GUARD_BYTES_ENABLE
	//check stuff bytes
	if (engine->m_cache_or_noncache == SHM_CACHE) {
		if (_Check_guard_data(engine, node) != 0) {
			_Check_guard_data_all_node(engine);
			flag = -1;
		}
	}
#endif

	/* clean node */
	node->m_offset = 0;
	node->m_alignment = 0;

	/* Update the number of free bytes. */
	engine->m_size_free += node->m_size;
	engine->m_size_used -= node->m_size;

	/* Find the next free node(go through node list, find the first node which is free). */
	for (new_node = node->m_next; new_node->m_next_free == NULL; new_node = new_node->m_next) ;

	/* Insert this node in the free list. */
	node->m_next_free = new_node;
	node->m_prev_free = new_node->m_prev_free;

	node->m_prev_free->m_next_free =
	new_node->m_prev_free          = node;

	engine->m_num_usedblock--;
	engine->m_num_freeblock++;

	/* Is the next node a free node and not the root? */
	if ((node->m_next == node->m_next_free) &&
		(node->m_next->m_size != 0)) {
		/* Merge this node with the next node. */
		new_node = node;
		res = _Merge(engine, new_node);

		if((new_node->m_next_free == new_node) ||
			(new_node->m_prev_free == new_node) || (res != 0)) {
			/* Error. */
			shm_error("_Merge next node failed.\n");
			goto err_exit;
		}

		engine->m_num_freeblock--;

		shm_debug("_Merge next node OK.\n");
	}

	/* Is the previous node a free node and not the root? */
	if ((node->m_prev == node->m_prev_free) && (node->m_prev->m_size != 0)) {
		/* Merge this node with the previous node. */
		new_node = node->m_prev;
		res = _Merge(engine, new_node);

		if((new_node->m_next_free == new_node) ||
			(new_node->m_prev_free == new_node) || (res != 0)) {
			/* Error. */
			shm_error("_Merge previous node failed.\n");
			goto err_exit;
		}

		engine->m_num_freeblock--;

		shm_debug("_Merge previous node OK.\n");
	}

	up(&(engine->m_mutex));

	shm_debug("memory_engine_free OK.\n");
#ifdef SHM_GUARD_BYTES_ENABLE
	if (flag != 0)
		return flag;
#endif
	return 0;

err_exit:

	up(&(engine->m_mutex));

	if (shm_lowmem_debug_level > 2) {
		shm_error("memory_engine_free failed !!! (0x%08X)\n", alignaddr);
		dump_stack();
	}

	return res;
}