/*************************************************************************/ /*!
@File
@Title		Resource Handle Manager
@Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
@Description	Provide resource handle management
@License        Dual MIT/GPLv2

The contents of this file are subject to the MIT license as set out below.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 2 ("GPL") in which case the provisions
of GPL are applicable instead of those above.

If you wish to allow use of your version of this file only under the terms of
GPL, and not to allow others to use your version of this file under the terms
of the MIT license, indicate your decision by deleting the provisions above
and replace them with the notice and other provisions required by GPL as set
out in the file called "GPL-COPYING" included in this distribution. If you do
not delete the provisions above, a recipient may use your version of this file
under the terms of either the MIT license or GPL.

This License is also included in this distribution in the file called
"MIT-COPYING".

EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ /***************************************************************************/

/* See handle.h for a description of the handle API. */

/*
 * The implementation supports movable handle structures, allowing the address
 * of a handle structure to change without having to fix up pointers in
 * any of the handle structures. For example, the linked list mechanism
 * used to link subhandles together uses handle array indices rather than
 * pointers to the structures themselves.
 */

#if defined(LINUX)
#include <linux/stddef.h>
#else
#include <stddef.h>
#endif

#include "img_defs.h"
#include "handle.h"
#include "handle_impl.h"
#include "allocmem.h"
#include "pvr_debug.h"
#include "connection_server.h"

#define	HANDLE_HASH_TAB_INIT_SIZE		32

#define	SET_FLAG(v, f)				((void)((v) |= (f)))
#define	CLEAR_FLAG(v, f)			((void)((v) &= (IMG_UINT)~(f)))
#define	TEST_FLAG(v, f)				((IMG_BOOL)(((v) & (f)) != 0))

#define	TEST_ALLOC_FLAG(psHandleData, f)	TEST_FLAG((psHandleData)->eFlag, f)


/* Linked list structure. Used for both the list head and list items */
typedef struct _HANDLE_LIST_
{
	IMG_HANDLE hPrev;
	IMG_HANDLE hNext;
	IMG_HANDLE hParent;
} HANDLE_LIST;

typedef struct _HANDLE_DATA_
{
	/* The handle that represents this structure */
	IMG_HANDLE hHandle;

	/* Handle type */
	PVRSRV_HANDLE_TYPE eType;

	/* Flags specified when the handle was allocated */
	PVRSRV_HANDLE_ALLOC_FLAG eFlag;

	/* Pointer to the data that the handle represents */
	void *pvData;

	/*
	 * Callback specified at handle allocation time to
	 * release/destroy/free the data represented by the
	 * handle when it's reference count reaches 0. This
	 * should always be NULL for subhandles.
	 */
	PFN_HANDLE_RELEASE pfnReleaseData;

	/* List head for subhandles of this handle */
	HANDLE_LIST sChildren;

	/* List entry for sibling subhandles */
	HANDLE_LIST sSiblings;

	/* Reference count. The pfnReleaseData callback gets called when the
	 * reference count hits zero
	 */
	IMG_UINT32 ui32RefCount;
} HANDLE_DATA;

struct _HANDLE_BASE_
{
	/* Pointer to a handle implementations base structure */
	HANDLE_IMPL_BASE *psImplBase;

	/*
	 * Pointer to handle hash table.
	 * The hash table is used to do reverse lookups, converting data
	 * pointers to handles.
	 */
	HASH_TABLE *psHashTab;

	/* Can be connection, process, global */
	PVRSRV_HANDLE_BASE_TYPE eType;
};

/*
 * The key for the handle hash table is an array of three elements, the
 * pointer to the resource, the resource type and the parent handle (or
 * NULL if there is no parent). The eHandKey enumeration gives the
 * array indices of the elements making up the key.
 */
enum eHandKey
{
	HAND_KEY_DATA = 0,
	HAND_KEY_TYPE,
	HAND_KEY_PARENT,
	HAND_KEY_LEN		/* Must be last item in list */
};

/* HAND_KEY is the type of the hash table key */
typedef uintptr_t HAND_KEY[HAND_KEY_LEN];

/* Stores a pointer to the function table of the handle back-end in use */
static HANDLE_IMPL_FUNCTAB const *gpsHandleFuncs;

/*
 * Global lock added to avoid to call the handling functions
 * only in a single threaded context.
 */
static POS_LOCK gHandleLock;
static IMG_BOOL gbLockInitialised = IMG_FALSE;

void LockHandle(void)
{
	OSLockAcquire(gHandleLock);
}

void UnlockHandle(void)
{
	OSLockRelease(gHandleLock);
}

/*
 * Kernel handle base structure. This is used for handles that are not
 * allocated on behalf of a particular process.
 */
PVRSRV_HANDLE_BASE *gpsKernelHandleBase = NULL;

/* Increase the reference count on the given handle.
 * The handle lock must already be acquired.
 * Returns: the reference count after the increment
 */
static inline IMG_UINT32 _HandleRef(HANDLE_DATA *psHandleData)
{
#if defined PVRSRV_DEBUG_HANDLE_LOCK
	if (!OSLockIsLocked(gHandleLock))
	{
		PVR_DPF((PVR_DBG_ERROR, "%s: Handle lock is not locked", __func__));
		OSDumpStack();
	}
#endif
	psHandleData->ui32RefCount++;
	return psHandleData->ui32RefCount;
}

/* Decrease the reference count on the given handle.
 * The handle lock must already be acquired.
 * Returns: the reference count after the decrement
 */
static inline IMG_UINT32 _HandleUnref(HANDLE_DATA *psHandleData)
{
#if defined PVRSRV_DEBUG_HANDLE_LOCK
	if (!OSLockIsLocked(gHandleLock))
	{
		PVR_DPF((PVR_DBG_ERROR, "%s: Handle lock is not locked", __func__));
		OSDumpStack();
	}
#endif
	PVR_ASSERT(psHandleData->ui32RefCount > 0);
	psHandleData->ui32RefCount--;

	return psHandleData->ui32RefCount;
}

#if defined(PVRSRV_NEED_PVR_DPF)
static const IMG_CHAR *HandleTypeToString(PVRSRV_HANDLE_TYPE eType)
{
	#define HANDLETYPE(x) \
			case PVRSRV_HANDLE_TYPE_##x: \
				return #x;
	switch(eType)
	{
		#include "handle_types.h"
		#undef HANDLETYPE

		default:
			return "INVALID";
	}
}
#endif /* PVRSRV_NEED_PVR_DPF */

/*!
******************************************************************************

 @Function	GetHandleData

 @Description	Get the handle data structure for a given handle

 @Input		psBase - pointer to handle base structure
		ppsHandleData - location to return pointer to handle data structure
		hHandle - handle from client
		eType - handle type or PVRSRV_HANDLE_TYPE_NONE if the
			handle type is not to be checked.

 @Output	ppsHandleData - points to a pointer to the handle data structure

 @Return	Error code or PVRSRV_OK

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(GetHandleData)
#endif
static INLINE
PVRSRV_ERROR GetHandleData(PVRSRV_HANDLE_BASE *psBase,
			   HANDLE_DATA **ppsHandleData,
			   IMG_HANDLE hHandle,
			   PVRSRV_HANDLE_TYPE eType)
{
	HANDLE_DATA *psHandleData;
	PVRSRV_ERROR eError;

	eError = gpsHandleFuncs->pfnGetHandleData(psBase->psImplBase,
						  hHandle,
						  (void **)&psHandleData);
	if (eError != PVRSRV_OK)
	{
		return eError;
	}

	/*
	 * Unless PVRSRV_HANDLE_TYPE_NONE was passed in to this function,
	 * check handle is of the correct type.
	 */
	if (eType != PVRSRV_HANDLE_TYPE_NONE && eType != psHandleData->eType)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "GetHandleData: Type mismatch. Lookup request: Handle %p, type: %s (%u) but stored handle is type %s (%u)",
			 hHandle,
			 HandleTypeToString(eType),
			 eType,
			 HandleTypeToString(psHandleData->eType),
			 psHandleData->eType));
		return PVRSRV_ERROR_HANDLE_TYPE_MISMATCH;
	}

	/* Return the handle structure */
	*ppsHandleData = psHandleData;

	return PVRSRV_OK;
}

/*!
******************************************************************************

 @Function	HandleListInit

 @Description	Initialise a linked list structure embedded in a handle
		structure.

 @Input		hHandle - handle containing the linked list structure
		psList - pointer to linked list structure
		hParent - parent handle or NULL

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(HandleListInit)
#endif
static INLINE
void HandleListInit(IMG_HANDLE hHandle, HANDLE_LIST *psList, IMG_HANDLE hParent)
{
	psList->hPrev = hHandle;
	psList->hNext = hHandle;
	psList->hParent = hParent;
}

/*!
******************************************************************************

 @Function	InitParentList

 @Description	Initialise the children list head in a handle structure.
		The children are the subhandles of this handle.

 @Input		psHandleData - pointer to handle data structure

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(InitParentList)
#endif
static INLINE
void InitParentList(HANDLE_DATA *psHandleData)
{
	IMG_HANDLE hParent = psHandleData->hHandle;

	HandleListInit(hParent, &psHandleData->sChildren, hParent);
}

/*!
******************************************************************************

 @Function	InitChildEntry

 @Description	Initialise the child list entry in a handle structure.
		The list entry is used to link together subhandles of
		a given handle.

 @Input		psHandleData - pointer to handle data structure

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(InitChildEntry)
#endif
static INLINE
void InitChildEntry(HANDLE_DATA *psHandleData)
{
	HandleListInit(psHandleData->hHandle, &psHandleData->sSiblings, NULL);
}

/*!
******************************************************************************

 @Function	HandleListIsEmpty

 @Description	Determine whether a given linked list is empty.

 @Input		hHandle - handle containing the list head
		psList - pointer to the list head

 @Return	IMG_TRUE if the list is empty, IMG_FALSE if it isn't.

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(HandleListIsEmpty)
#endif
static INLINE
IMG_BOOL HandleListIsEmpty(IMG_HANDLE hHandle, HANDLE_LIST *psList) /* Instead of passing in the handle can we not just do (psList->hPrev == psList->hNext) ? IMG_TRUE : IMG_FALSE ??? */
{
	IMG_BOOL bIsEmpty;

	bIsEmpty = (IMG_BOOL)(psList->hNext == hHandle);

#ifdef	DEBUG
	{
		IMG_BOOL bIsEmpty2;

		bIsEmpty2 = (IMG_BOOL)(psList->hPrev == hHandle);
		PVR_ASSERT(bIsEmpty == bIsEmpty2);
	}
#endif

	return bIsEmpty;
}

#ifdef DEBUG
/*!
******************************************************************************

 @Function	NoChildren

 @Description	Determine whether a handle has any subhandles

 @Input		psHandleData - pointer to handle data structure

 @Return	IMG_TRUE if the handle has no subhandles, IMG_FALSE if it does.

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(NoChildren)
#endif
static INLINE
IMG_BOOL NoChildren(HANDLE_DATA *psHandleData)
{
	PVR_ASSERT(psHandleData->sChildren.hParent == psHandleData->hHandle);

	return HandleListIsEmpty(psHandleData->hHandle, &psHandleData->sChildren);
}

/*!
******************************************************************************

 @Function	NoParent

 @Description	Determine whether a handle is a subhandle

 @Input		psHandleData - pointer to handle data structure

 @Return	IMG_TRUE if the handle is not a subhandle, IMG_FALSE if it is.

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(NoParent)
#endif
static INLINE
IMG_BOOL NoParent(HANDLE_DATA *psHandleData)
{
	if (HandleListIsEmpty(psHandleData->hHandle, &psHandleData->sSiblings))
	{
		PVR_ASSERT(psHandleData->sSiblings.hParent == NULL);

		return IMG_TRUE;
	}
	else
	{
		PVR_ASSERT(psHandleData->sSiblings.hParent != NULL);
	}
	return IMG_FALSE;
}
#endif /*DEBUG*/

/*!
******************************************************************************

 @Function	ParentHandle

 @Description	Determine the parent of a handle

 @Input		psHandleData - pointer to handle data structure

 @Return	Parent handle, or NULL if the handle is not a subhandle.

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(ParentHandle)
#endif
static INLINE
IMG_HANDLE ParentHandle(HANDLE_DATA *psHandleData)
{
	return psHandleData->sSiblings.hParent;
}

/*
 * GetHandleListFromHandleAndOffset is used to generate either a
 * pointer to the subhandle list head, or a pointer to the linked list
 * structure of an item on a subhandle list.
 * The list head is itself on the list, but is at a different offset
 * in the handle structure to the linked list structure for items on
 * the list. The two linked list structures are differentiated by
 * the third parameter, containing the parent handle. The parent field
 * in the list head structure references the handle structure that contains
 * it. For items on the list, the parent field in the linked list structure
 * references the parent handle, which will be different from the handle
 * containing the linked list structure.
 */
#ifdef INLINE_IS_PRAGMA
#pragma inline(GetHandleListFromHandleAndOffset)
#endif
static INLINE
HANDLE_LIST *GetHandleListFromHandleAndOffset(PVRSRV_HANDLE_BASE *psBase,
					      IMG_HANDLE hEntry,
					      IMG_HANDLE hParent,
					      size_t uiParentOffset,
					      size_t uiEntryOffset)
{
	HANDLE_DATA *psHandleData = NULL;
	PVRSRV_ERROR eError;

	PVR_ASSERT(psBase != NULL);

	eError = GetHandleData(psBase,
			       &psHandleData,
			       hEntry,
			       PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		return NULL;
	}

	if (hEntry == hParent)
	{
		return (HANDLE_LIST *)((IMG_CHAR *)psHandleData + uiParentOffset);
	}
	else
	{
		return (HANDLE_LIST *)((IMG_CHAR *)psHandleData + uiEntryOffset);
	}
}

/*!
******************************************************************************

 @Function	HandleListInsertBefore

 @Description	Insert a handle before a handle currently on the list.

 @Input		hEntry - handle to be inserted after
		psEntry - pointer to handle structure to be inserted after
		uiParentOffset - offset to list head struct in handle structure
		hNewEntry - handle to be inserted
		psNewEntry - pointer to handle structure of item to be inserted
		uiEntryOffset - offset of list item struct in handle structure
		hParent - parent handle of hNewEntry

 @Return	Error code or PVRSRV_OK

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(HandleListInsertBefore)
#endif
static INLINE
PVRSRV_ERROR HandleListInsertBefore(PVRSRV_HANDLE_BASE *psBase,
				    IMG_HANDLE hEntry,
				    HANDLE_LIST *psEntry,
				    size_t uiParentOffset,
				    IMG_HANDLE hNewEntry,
				    HANDLE_LIST *psNewEntry,
				    size_t uiEntryOffset,
				    IMG_HANDLE hParent)
{
	HANDLE_LIST *psPrevEntry;

	if (psBase == NULL || psEntry == NULL || psNewEntry == NULL)
	{
		return PVRSRV_ERROR_INVALID_PARAMS;
	}

	psPrevEntry = GetHandleListFromHandleAndOffset(psBase,
						       psEntry->hPrev,
						       hParent,
						       uiParentOffset,
						       uiEntryOffset);
	if (psPrevEntry == NULL)
	{
		return PVRSRV_ERROR_HANDLE_INDEX_OUT_OF_RANGE;
	}

	PVR_ASSERT(psNewEntry->hParent == NULL);
	PVR_ASSERT(hEntry == psPrevEntry->hNext);

#if defined(DEBUG)
	{
		HANDLE_LIST *psParentList;

		psParentList = GetHandleListFromHandleAndOffset(psBase,
								hParent,
								hParent,
								uiParentOffset,
								uiParentOffset);
		PVR_ASSERT(psParentList && psParentList->hParent == hParent);
	}
#endif /* defined(DEBUG) */

	psNewEntry->hPrev = psEntry->hPrev;
	psEntry->hPrev = hNewEntry;

	psNewEntry->hNext = hEntry;
	psPrevEntry->hNext = hNewEntry;

	psNewEntry->hParent = hParent;

	return PVRSRV_OK;
}

/*!
******************************************************************************

 @Function	AdoptChild

 @Description	Assign a subhandle to a handle

 @Input		psParentData - pointer to handle structure of parent handle
		psChildData - pointer to handle structure of child subhandle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(AdoptChild)
#endif
static INLINE
PVRSRV_ERROR AdoptChild(PVRSRV_HANDLE_BASE *psBase,
			HANDLE_DATA *psParentData,
			HANDLE_DATA *psChildData)
{
	IMG_HANDLE hParent = psParentData->sChildren.hParent;

	PVR_ASSERT(hParent == psParentData->hHandle);

	return HandleListInsertBefore(psBase,
				      hParent,
				      &psParentData->sChildren,
				      offsetof(HANDLE_DATA, sChildren),
				      psChildData->hHandle,
				      &psChildData->sSiblings,
				      offsetof(HANDLE_DATA, sSiblings),
				      hParent);
}

/*!
******************************************************************************

 @Function	HandleListRemove

 @Description	Remove a handle from a list

 @Input		hEntry - handle to be removed
		psEntry - pointer to handle structure of item to be removed
		uiEntryOffset - offset of list item struct in handle structure
		uiParentOffset - offset to list head struct in handle structure

 @Return	Error code or PVRSRV_OK

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(HandleListRemove)
#endif
static INLINE
PVRSRV_ERROR HandleListRemove(PVRSRV_HANDLE_BASE *psBase,
			      IMG_HANDLE hEntry,
			      HANDLE_LIST *psEntry,
			      size_t uiEntryOffset,
			      size_t uiParentOffset)
{
	if (psBase == NULL || psEntry == NULL)
	{
		return PVRSRV_ERROR_INVALID_PARAMS;
	}

	if (!HandleListIsEmpty(hEntry, psEntry))
	{
		HANDLE_LIST *psPrev;
		HANDLE_LIST *psNext;

		psPrev = GetHandleListFromHandleAndOffset(psBase,
							  psEntry->hPrev,
							  psEntry->hParent,
							  uiParentOffset,
							  uiEntryOffset);
		if (psPrev == NULL)
		{
			return PVRSRV_ERROR_HANDLE_INDEX_OUT_OF_RANGE;
		}

		psNext = GetHandleListFromHandleAndOffset(psBase,
							  psEntry->hNext,
							  psEntry->hParent,
							  uiParentOffset,
							  uiEntryOffset);
		if (psNext == NULL)
		{
			return PVRSRV_ERROR_HANDLE_INDEX_OUT_OF_RANGE;
		}

		/*
		 * The list head is on the list, and we don't want to
		 * remove it.
		 */
		PVR_ASSERT(psEntry->hParent != NULL);

		psPrev->hNext = psEntry->hNext;
		psNext->hPrev = psEntry->hPrev;

		HandleListInit(hEntry, psEntry, NULL);
	}

	return PVRSRV_OK;
}

/*!
******************************************************************************

 @Function	UnlinkFromParent

 @Description	Remove a subhandle from its parents list

 @Input		psHandleData - pointer to handle data structure of child subhandle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(UnlinkFromParent)
#endif
static INLINE
PVRSRV_ERROR UnlinkFromParent(PVRSRV_HANDLE_BASE *psBase,
			      HANDLE_DATA *psHandleData)
{
	return HandleListRemove(psBase,
				psHandleData->hHandle,
				&psHandleData->sSiblings,
				offsetof(HANDLE_DATA, sSiblings),
				offsetof(HANDLE_DATA, sChildren));
}

/*!
******************************************************************************

 @Function	HandleListIterate

 @Description	Iterate over the items in a list

 @Input		psHead - pointer to list head
		uiParentOffset - offset to list head struct in handle structure
		uiEntryOffset - offset of list item struct in handle structure
		pfnIterFunc - function to be called for each handle in the list

 @Return	Error code or PVRSRV_OK

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(HandleListIterate)
#endif
static INLINE
PVRSRV_ERROR HandleListIterate(PVRSRV_HANDLE_BASE *psBase,
			       HANDLE_LIST *psHead,
			       size_t uiParentOffset,
			       size_t uiEntryOffset,
			       PVRSRV_ERROR (*pfnIterFunc)(PVRSRV_HANDLE_BASE *, IMG_HANDLE))
{
	IMG_HANDLE hHandle = psHead->hNext;
	IMG_HANDLE hParent = psHead->hParent;
	IMG_HANDLE hNext;

	PVR_ASSERT(psHead->hParent != NULL);

	/*
 	 * Follow the next chain from the list head until we reach
 	 * the list head again, which signifies the end of the list.
 	 */
	while (hHandle != hParent)
	{
		HANDLE_LIST *psEntry;
		PVRSRV_ERROR eError;

		psEntry = GetHandleListFromHandleAndOffset(psBase,
							   hHandle,
							   hParent,
							   uiParentOffset,
							   uiEntryOffset);
		if (psEntry == NULL)
		{
			return PVRSRV_ERROR_HANDLE_INDEX_OUT_OF_RANGE;
		}

		PVR_ASSERT(psEntry->hParent == psHead->hParent);

		/*
		 * Get the next index now, in case the list item is
		 * modified by the iteration function.
		 */
		hNext = psEntry->hNext;

		eError = (*pfnIterFunc)(psBase, hHandle);
		if (eError != PVRSRV_OK)
		{
			return eError;
		}

		hHandle = hNext;
	}

	return PVRSRV_OK;
}

/*!
******************************************************************************

 @Function	IterateOverChildren

 @Description	Iterate over the subhandles of a parent handle

 @Input		psParentData - pointer to parent handle structure
		pfnIterFunc - function to be called for each subhandle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(IterateOverChildren)
#endif
static INLINE
PVRSRV_ERROR IterateOverChildren(PVRSRV_HANDLE_BASE *psBase,
				 HANDLE_DATA *psParentData,
				 PVRSRV_ERROR (*pfnIterFunc)(PVRSRV_HANDLE_BASE *, IMG_HANDLE))
{
	 return HandleListIterate(psBase,
				  &psParentData->sChildren,
				  offsetof(HANDLE_DATA, sChildren),
				  offsetof(HANDLE_DATA, sSiblings),
				  pfnIterFunc);
}

/*!
******************************************************************************

 @Function	ParentIfPrivate

 @Description	Return the parent handle if the handle was allocated
		with PVRSRV_HANDLE_ALLOC_FLAG_PRIVATE, else return
		NULL

 @Input		psHandleData - pointer to handle data structure

 @Return	Parent handle, or NULL

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(ParentIfPrivate)
#endif
static INLINE
IMG_HANDLE ParentIfPrivate(HANDLE_DATA *psHandleData)
{
	return TEST_ALLOC_FLAG(psHandleData, PVRSRV_HANDLE_ALLOC_FLAG_PRIVATE) ?
			ParentHandle(psHandleData) : NULL;
}

/*!
******************************************************************************

 @Function	InitKey

 @Description	Initialise a hash table key for the current process

 @Input		psBase - pointer to handle base structure
		aKey - pointer to key
		pvData - pointer to the resource the handle represents
		eType - type of resource

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(InitKey)
#endif
static INLINE
void InitKey(HAND_KEY aKey,
	     PVRSRV_HANDLE_BASE *psBase,
	     void *pvData,
	     PVRSRV_HANDLE_TYPE eType,
	     IMG_HANDLE hParent)
{
	PVR_UNREFERENCED_PARAMETER(psBase);

	aKey[HAND_KEY_DATA] = (uintptr_t)pvData;
	aKey[HAND_KEY_TYPE] = (uintptr_t)eType;
	aKey[HAND_KEY_PARENT] = (uintptr_t)hParent;
}

static PVRSRV_ERROR FreeHandleWrapper(PVRSRV_HANDLE_BASE *psBase, IMG_HANDLE hHandle);

/*!
******************************************************************************

 @Function	FreeHandle

 @Description	Free a handle data structure.

 @Input		psBase - Pointer to handle base structure
		hHandle - Handle to be freed
		eType - Type of the handle to be freed
		ppvData - Location for data associated with the freed handle

 @Output 		ppvData - Points to data that was associated with the freed handle

 @Return	PVRSRV_OK or PVRSRV_ERROR

******************************************************************************/
static PVRSRV_ERROR FreeHandle(PVRSRV_HANDLE_BASE *psBase,
			       IMG_HANDLE hHandle,
			       PVRSRV_HANDLE_TYPE eType,
			       void **ppvData)
{
	HANDLE_DATA *psHandleData = NULL;
	HANDLE_DATA *psReleasedHandleData;
	PVRSRV_ERROR eError;

	eError = GetHandleData(psBase, &psHandleData, hHandle, eType);
	if (eError != PVRSRV_OK)
	{
		return eError;
	}

	if (_HandleUnref(psHandleData) > 0)
	{
		/* this handle still has references so do not destroy it
		 * or the underlying object yet
		 */
		return PVRSRV_OK;
	}

	/* Call the release data callback for each reference on the handle */
	if (psHandleData->pfnReleaseData != NULL)
	{
		eError = psHandleData->pfnReleaseData(psHandleData->pvData);
		if (eError == PVRSRV_ERROR_RETRY)
		{
			PVR_DPF((PVR_DBG_MESSAGE,
				 "FreeHandle: "
				 "Got retry while calling release data callback for %p (type = %d)",
				 hHandle,
				 (IMG_UINT32)psHandleData->eType));

			/* the caller should retry, so retain a reference on the handle */
			_HandleRef(psHandleData);

			return eError;
		}
		else if (eError != PVRSRV_OK)
		{
			return eError;
		}
	}

	if (!TEST_ALLOC_FLAG(psHandleData, PVRSRV_HANDLE_ALLOC_FLAG_MULTI))
	{
		HAND_KEY aKey;
		IMG_HANDLE hRemovedHandle;

		InitKey(aKey, psBase, psHandleData->pvData, psHandleData->eType, ParentIfPrivate(psHandleData));

		hRemovedHandle = (IMG_HANDLE)HASH_Remove_Extended(psBase->psHashTab, aKey);

		PVR_ASSERT(hRemovedHandle != NULL);
		PVR_ASSERT(hRemovedHandle == psHandleData->hHandle);
		PVR_UNREFERENCED_PARAMETER(hRemovedHandle);
	}

	eError = UnlinkFromParent(psBase, psHandleData);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "FreeHandle: Error whilst unlinking from parent handle (%s)",
			 PVRSRVGetErrorStringKM(eError)));
		return eError;
	}

	/* Free children */
	eError = IterateOverChildren(psBase, psHandleData, FreeHandleWrapper);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "FreeHandle: Error whilst freeing subhandles (%s)",
			 PVRSRVGetErrorStringKM(eError)));
		return eError;
	}

	eError = gpsHandleFuncs->pfnReleaseHandle(psBase->psImplBase,
						  psHandleData->hHandle,
						  (void **)&psReleasedHandleData);
	if (eError == PVRSRV_OK)
	{
		PVR_ASSERT(psReleasedHandleData == psHandleData);
	}

	if (ppvData)
	{
		*ppvData = psHandleData->pvData;
	}

	OSFreeMem(psHandleData);

	return eError;
}

static PVRSRV_ERROR FreeHandleWrapper(PVRSRV_HANDLE_BASE *psBase, IMG_HANDLE hHandle)
{
	return FreeHandle(psBase, hHandle, PVRSRV_HANDLE_TYPE_NONE, NULL);
}

/*!
******************************************************************************

 @Function	FindHandle

 @Description	Find handle corresponding to a resource pointer

 @Input		psBase - pointer to handle base structure
		pvData - pointer to resource to be associated with the handle
		eType - the type of resource

 @Return	the handle, or NULL if not found

******************************************************************************/
#ifdef INLINE_IS_PRAGMA
#pragma inline(FindHandle)
#endif
static INLINE
IMG_HANDLE FindHandle(PVRSRV_HANDLE_BASE *psBase,
		      void *pvData,
		      PVRSRV_HANDLE_TYPE eType,
		      IMG_HANDLE hParent)
{
	HAND_KEY aKey;

	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);

	InitKey(aKey, psBase, pvData, eType, hParent);

	return (IMG_HANDLE) HASH_Retrieve_Extended(psBase->psHashTab, aKey);
}

/*!
******************************************************************************

 @Function	AllocHandle

 @Description	Allocate a new handle

 @Input		phHandle - location for new handle
		pvData - pointer to resource to be associated with the handle
		eType - the type of resource
		hParent - parent handle or NULL
		pfnReleaseData - Function to release resource at handle release
		                 time

 @Output	phHandle - points to new handle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
static PVRSRV_ERROR AllocHandle(PVRSRV_HANDLE_BASE *psBase,
				IMG_HANDLE *phHandle,
				void *pvData,
				PVRSRV_HANDLE_TYPE eType,
				PVRSRV_HANDLE_ALLOC_FLAG eFlag,
				IMG_HANDLE hParent,
				PFN_HANDLE_RELEASE pfnReleaseData)
{
	HANDLE_DATA *psNewHandleData;
	IMG_HANDLE hHandle;
	PVRSRV_ERROR eError;

	/* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */
	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);
	PVR_ASSERT(psBase != NULL && psBase->psHashTab != NULL);
	PVR_ASSERT(gpsHandleFuncs);

	if (!TEST_FLAG(eFlag, PVRSRV_HANDLE_ALLOC_FLAG_MULTI))
	{
		/* Handle must not already exist */
		PVR_ASSERT(FindHandle(psBase, pvData, eType, hParent) == NULL);
	}

	psNewHandleData = OSAllocZMem(sizeof(*psNewHandleData));
	if (psNewHandleData == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "AllocHandle: Couldn't allocate handle data"));
		return PVRSRV_ERROR_OUT_OF_MEMORY;
	}

	eError = gpsHandleFuncs->pfnAcquireHandle(psBase->psImplBase, &hHandle, psNewHandleData);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "AllocHandle: Failed to acquire a handle"));
		goto ErrorFreeHandleData;
	}

	/*
	 * If a data pointer can be associated with multiple handles, we
	 * don't put the handle in the hash table, as the data pointer
	 * may not map to a unique handle
	 */
	if (!TEST_FLAG(eFlag, PVRSRV_HANDLE_ALLOC_FLAG_MULTI))
	{
		HAND_KEY aKey;

		/* Initialise hash key */
		InitKey(aKey, psBase, pvData, eType, hParent);

		/* Put the new handle in the hash table */
		if (!HASH_Insert_Extended(psBase->psHashTab, aKey, (uintptr_t)hHandle))
		{
			PVR_DPF((PVR_DBG_ERROR, "AllocHandle: Couldn't add handle to hash table"));
			eError = PVRSRV_ERROR_UNABLE_TO_ADD_HANDLE;
			goto ErrorReleaseHandle;
		}
	}

	psNewHandleData->hHandle = hHandle;
	psNewHandleData->eType = eType;
	psNewHandleData->eFlag = eFlag;
	psNewHandleData->pvData = pvData;
	psNewHandleData->pfnReleaseData = pfnReleaseData;
	psNewHandleData->ui32RefCount = 1;

	InitParentList(psNewHandleData);
#if defined(DEBUG)
	PVR_ASSERT(NoChildren(psNewHandleData));
#endif

	InitChildEntry(psNewHandleData);
#if defined(DEBUG)
	PVR_ASSERT(NoParent(psNewHandleData));
#endif

	/* Return the new handle to the client */
	*phHandle = psNewHandleData->hHandle;

	return PVRSRV_OK;

ErrorReleaseHandle:
	(void)gpsHandleFuncs->pfnReleaseHandle(psBase->psImplBase, hHandle, NULL);

ErrorFreeHandleData:
	OSFreeMem(psNewHandleData);

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVAllocHandle

 @Description	Allocate a handle

 @Input		phHandle - location for new handle
		pvData - pointer to resource to be associated with the handle
		eType - the type of resource
		pfnReleaseData - Function to release resource at handle release
		                 time

 @Output	phHandle - points to new handle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVAllocHandle(PVRSRV_HANDLE_BASE *psBase,
			       IMG_HANDLE *phHandle,
			       void *pvData,
			       PVRSRV_HANDLE_TYPE eType,
			       PVRSRV_HANDLE_ALLOC_FLAG eFlag,
			       PFN_HANDLE_RELEASE pfnReleaseData)
{
	PVRSRV_ERROR eError;

	LockHandle();
	eError = PVRSRVAllocHandleUnlocked(psBase, phHandle, pvData, eType, eFlag, pfnReleaseData);
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVAllocHandleUnlocked

 @Description	Allocate a handle without acquiring/releasing the handle
		lock. The function assumes you hold the lock when called.

 @Input		phHandle - location for new handle
		pvData - pointer to resource to be associated with the handle
		eType - the type of resource
		pfnReleaseData - Function to release resource at handle release
		                 time

 @Output	phHandle - points to new handle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVAllocHandleUnlocked(PVRSRV_HANDLE_BASE *psBase,
			       IMG_HANDLE *phHandle,
			       void *pvData,
			       PVRSRV_HANDLE_TYPE eType,
			       PVRSRV_HANDLE_ALLOC_FLAG eFlag,
			       PFN_HANDLE_RELEASE pfnReleaseData)
{
	PVRSRV_ERROR eError;

	*phHandle = NULL;

	/* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */
	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);
	PVR_ASSERT(gpsHandleFuncs);

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocHandle: Missing handle base"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto Exit;
	}

	if (pfnReleaseData == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocHandle: Missing release function"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto Exit;
	}

	eError = AllocHandle(psBase, phHandle, pvData, eType, eFlag, NULL, pfnReleaseData);

Exit:
	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVAllocSubHandle

 @Description	Allocate a subhandle

 @Input		phHandle - location for new subhandle
		pvData - pointer to resource to be associated with the subhandle
		eType - the type of resource
		hParent - parent handle

 @Output	phHandle - points to new subhandle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVAllocSubHandle(PVRSRV_HANDLE_BASE *psBase,
				  IMG_HANDLE *phHandle,
				  void *pvData,
				  PVRSRV_HANDLE_TYPE eType,
				  PVRSRV_HANDLE_ALLOC_FLAG eFlag,
				  IMG_HANDLE hParent)
{
	PVRSRV_ERROR eError;

	LockHandle();
	eError = PVRSRVAllocSubHandleUnlocked(psBase, phHandle, pvData, eType, eFlag, hParent);
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVAllocSubHandleUnlocked

 @Description	Allocate a subhandle without acquiring/releasing the
		handle lock. The function assumes you hold the lock when called.

 @Input		phHandle - location for new subhandle
		pvData - pointer to resource to be associated with the subhandle
		eType - the type of resource
		hParent - parent handle

 @Output	phHandle - points to new subhandle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVAllocSubHandleUnlocked(PVRSRV_HANDLE_BASE *psBase,
				  IMG_HANDLE *phHandle,
				  void *pvData,
				  PVRSRV_HANDLE_TYPE eType,
				  PVRSRV_HANDLE_ALLOC_FLAG eFlag,
				  IMG_HANDLE hParent)
{
	HANDLE_DATA *psPHandleData = NULL;
	HANDLE_DATA *psCHandleData = NULL;
	IMG_HANDLE hParentKey;
	IMG_HANDLE hHandle;
	PVRSRV_ERROR eError;

	*phHandle = NULL;

	/* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */
	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);
	PVR_ASSERT(gpsHandleFuncs);

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocSubHandle: Missing handle base"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto Exit;
	}

	hParentKey = TEST_FLAG(eFlag, PVRSRV_HANDLE_ALLOC_FLAG_PRIVATE) ? hParent : NULL;

	/* Lookup the parent handle */
	eError = GetHandleData(psBase, &psPHandleData, hParent, PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocSubHandle: Failed to get parent handle structure"));
		goto Exit;
	}

	eError = AllocHandle(psBase, &hHandle, pvData, eType, eFlag, hParentKey, NULL);
	if (eError != PVRSRV_OK)
	{
		goto Exit;
	}

	eError = GetHandleData(psBase, &psCHandleData, hHandle, PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocSubHandle: Failed to get parent handle structure"));

		/* If we were able to allocate the handle then there should be no reason why we
		   can't also get it's handle structure. Otherwise something has gone badly wrong. */
		PVR_ASSERT(eError == PVRSRV_OK);

		goto Exit;
	}

	/*
	 * Get the parent handle structure again, in case the handle
	 * structure has moved (depending on the implementation
	 * of AllocHandle).
	 */
	eError = GetHandleData(psBase, &psPHandleData, hParent, PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocSubHandle: Failed to get parent handle structure"));

		(void)FreeHandle(psBase, hHandle, eType, NULL);
		goto Exit;
	}

	eError = AdoptChild(psBase, psPHandleData, psCHandleData);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocSubHandle: Parent handle failed to adopt subhandle"));

		(void)FreeHandle(psBase, hHandle, eType, NULL);
		goto Exit;
	}

	*phHandle = hHandle;

	eError = PVRSRV_OK;

Exit:
	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVFindHandle

 @Description	Find handle corresponding to a resource pointer

 @Input		phHandle - location for returned handle
		pvData - pointer to resource to be associated with the handle
		eType - the type of resource

 @Output	phHandle - points to handle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVFindHandle(PVRSRV_HANDLE_BASE *psBase,
			      IMG_HANDLE *phHandle,
			      void *pvData,
			      PVRSRV_HANDLE_TYPE eType)
{
	PVRSRV_ERROR eError;

	LockHandle();
	eError = PVRSRVFindHandleUnlocked(psBase, phHandle, pvData, eType);
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVFindHandleUnlocked

 @Description	Find handle corresponding to a resource pointer without
		acquiring/releasing the handle lock. The function assumes you hold
		the lock when called.

 @Input		phHandle - location for returned handle
		pvData - pointer to resource to be associated with the handle
		eType - the type of resource

 @Output	phHandle - points to handle

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVFindHandleUnlocked(PVRSRV_HANDLE_BASE *psBase,
			      IMG_HANDLE *phHandle,
			      void *pvData,
			      PVRSRV_HANDLE_TYPE eType)
{
	IMG_HANDLE hHandle;
	PVRSRV_ERROR eError;

	/* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */
	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);
	PVR_ASSERT(gpsHandleFuncs);

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVFindHandle: Missing handle base"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto Exit;
	}

	/* See if there is a handle for this data pointer */
	hHandle = FindHandle(psBase, pvData, eType, NULL);
	if (hHandle == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVFindHandle: Error finding handle. Type %u",
			 eType));

		eError = PVRSRV_ERROR_HANDLE_NOT_FOUND;
		goto Exit;
	}

	*phHandle = hHandle;

	eError = PVRSRV_OK;

Exit:
	return eError;

}

/*!
******************************************************************************

 @Function	PVRSRVLookupHandle

 @Description	Lookup the data pointer corresponding to a handle

 @Input		ppvData - location to return data pointer
		hHandle - handle from client
		eType - handle type
		bRef - If TRUE, a reference will be added on the handle if the
		       lookup is successful.

 @Output	ppvData - points to the data pointer

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVLookupHandle(PVRSRV_HANDLE_BASE *psBase,
				void **ppvData,
				IMG_HANDLE hHandle,
				PVRSRV_HANDLE_TYPE eType,
				IMG_BOOL bRef)
{
	PVRSRV_ERROR eError;

	LockHandle();
	eError = PVRSRVLookupHandleUnlocked(psBase, ppvData, hHandle, eType, bRef);
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVLookupHandleUnlocked

 @Description	Lookup the data pointer corresponding to a handle without
 		acquiring/releasing the handle lock. The function assumes you
		hold the lock when called.

 @Input		ppvData - location to return data pointer
		hHandle - handle from client
		eType - handle type
		bRef - If TRUE, a reference will be added on the handle if the
		       lookup is successful.

 @Output	ppvData - points to the data pointer

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVLookupHandleUnlocked(PVRSRV_HANDLE_BASE *psBase,
				void **ppvData,
				IMG_HANDLE hHandle,
				PVRSRV_HANDLE_TYPE eType,
				IMG_BOOL bRef)
{
	HANDLE_DATA *psHandleData = NULL;
	PVRSRV_ERROR eError;

	/* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */
	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);
	PVR_ASSERT(gpsHandleFuncs);

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVLookupHandle: Missing handle base"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto Exit;
	}

	eError = GetHandleData(psBase, &psHandleData, hHandle, eType);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVLookupHandle: Error looking up handle (%s). Handle %p, type %u",
			 PVRSRVGetErrorStringKM(eError),
			 (void*) hHandle,
			 eType));
#if defined(DEBUG) || defined(PVRSRV_NEED_PVR_DPF)
		OSDumpStack();
#endif
		goto Exit;
	}

	if (bRef)
	{
		_HandleRef(psHandleData);
	}

	*ppvData = psHandleData->pvData;

	eError = PVRSRV_OK;

Exit:

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVLookupSubHandle

 @Description	Lookup the data pointer corresponding to a subhandle

 @Input		ppvData - location to return data pointer
		hHandle - handle from client
		eType - handle type
		hAncestor - ancestor handle

 @Output	ppvData - points to the data pointer

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVLookupSubHandle(PVRSRV_HANDLE_BASE *psBase,
				   void **ppvData,
				   IMG_HANDLE hHandle,
				   PVRSRV_HANDLE_TYPE eType,
				   IMG_HANDLE hAncestor)
{
	HANDLE_DATA *psPHandleData = NULL;
	HANDLE_DATA *psCHandleData = NULL;
	PVRSRV_ERROR eError;

	/* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */
	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);
	PVR_ASSERT(gpsHandleFuncs);

	LockHandle();

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVLookupSubHandle: Missing handle base"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto ExitUnlock;
	}

	eError = GetHandleData(psBase, &psCHandleData, hHandle, eType);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVLookupSubHandle: Error looking up subhandle (%s). Handle %p, type %u",
			 PVRSRVGetErrorStringKM(eError),
			 (void*) hHandle,
			 eType));
		OSDumpStack();
		goto ExitUnlock;
	}

	/* Look for hAncestor among the handle's ancestors */
	for (psPHandleData = psCHandleData; ParentHandle(psPHandleData) != hAncestor; )
	{
		eError = GetHandleData(psBase, &psPHandleData, ParentHandle(psPHandleData), PVRSRV_HANDLE_TYPE_NONE);
		if (eError != PVRSRV_OK)
		{
			PVR_DPF((PVR_DBG_ERROR,"PVRSRVLookupSubHandle: Subhandle doesn't belong to given ancestor"));
			eError = PVRSRV_ERROR_INVALID_SUBHANDLE;
			goto ExitUnlock;
		}
	}

	*ppvData = psCHandleData->pvData;

	eError = PVRSRV_OK;

ExitUnlock:
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVGetParentHandle

 @Description	Lookup the parent of a handle

 @Input		phParent - location for returning parent handle
		hHandle - handle for which the parent handle is required
		eType - handle type
		hParent - parent handle

 @Output	*phParent - parent handle, or NULL if there is no parent

 @Return	Error code or PVRSRV_OK.  Note that not having a parent is
		not regarded as an error.

******************************************************************************/
PVRSRV_ERROR PVRSRVGetParentHandle(PVRSRV_HANDLE_BASE *psBase,
				   IMG_HANDLE *phParent,
				   IMG_HANDLE hHandle,
				   PVRSRV_HANDLE_TYPE eType)
{
	HANDLE_DATA *psHandleData = NULL;
	PVRSRV_ERROR eError;

	/* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */
	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);
	PVR_ASSERT(gpsHandleFuncs);

	LockHandle();

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVGetParentHandle: Missing handle base"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto ExitUnlock;
	}

	eError = GetHandleData(psBase, &psHandleData, hHandle, eType);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVGetParentHandle: Error looking up subhandle (%s). Type %u",
			 PVRSRVGetErrorStringKM(eError),
			 eType));
		OSDumpStack();
		goto ExitUnlock;
	}

	*phParent = ParentHandle(psHandleData);

	eError = PVRSRV_OK;

ExitUnlock:
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVReleaseHandle

 @Description	Release a handle that is no longer needed

 @Input 	hHandle - handle from client
		eType - handle type

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVReleaseHandle(PVRSRV_HANDLE_BASE *psBase,
				 IMG_HANDLE hHandle,
				 PVRSRV_HANDLE_TYPE eType)
{
	PVRSRV_ERROR eError;

	LockHandle();
	eError = PVRSRVReleaseHandleUnlocked(psBase, hHandle, eType);
	UnlockHandle();

	return eError;
}


/*!
******************************************************************************

 @Function	PVRSRVReleaseHandleUnlocked

 @Description	Release a handle that is no longer needed without
 		acquiring/releasing the handle lock. The function assumes you
		hold the lock when called.

 @Input 	hHandle - handle from client
		eType - handle type

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVReleaseHandleUnlocked(PVRSRV_HANDLE_BASE *psBase,
				 IMG_HANDLE hHandle,
				 PVRSRV_HANDLE_TYPE eType)
{
	PVRSRV_ERROR eError;

	/* PVRSRV_HANDLE_TYPE_NONE is reserved for internal use */
	PVR_ASSERT(eType != PVRSRV_HANDLE_TYPE_NONE);
	PVR_ASSERT(gpsHandleFuncs);

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVReleaseHandle: Missing handle base"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto Exit;
	}

	eError = FreeHandle(psBase, hHandle, eType, NULL);

Exit:

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVPurgeHandles

 @Description	Purge handles for a given handle base

 @Input 	psBase - pointer to handle base structure

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVPurgeHandles(PVRSRV_HANDLE_BASE *psBase)
{
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsHandleFuncs);

	LockHandle();

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVPurgeHandles: Missing handle base"));
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto ExitUnlock;
	}

	eError = gpsHandleFuncs->pfnPurgeHandles(psBase->psImplBase);

ExitUnlock:
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVAllocHandleBase

 @Description	Allocate a handle base structure for a process

 @Input 	ppsBase - pointer to handle base structure pointer

 @Output	ppsBase - points to handle base structure pointer

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVAllocHandleBase(PVRSRV_HANDLE_BASE **ppsBase,
                                   PVRSRV_HANDLE_BASE_TYPE eType)
{
	PVRSRV_HANDLE_BASE *psBase;
	PVRSRV_ERROR eError;

	if (gpsHandleFuncs == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocHandleBase: Handle management not initialised"));
		return PVRSRV_ERROR_NOT_READY;
	}

	LockHandle();

	if (ppsBase == NULL)
	{
		eError = PVRSRV_ERROR_INVALID_PARAMS;
		goto ErrorUnlock;
	}

	psBase = OSAllocZMem(sizeof(*psBase));
	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocHandleBase: Couldn't allocate handle base"));
		eError = PVRSRV_ERROR_OUT_OF_MEMORY;
		goto ErrorUnlock;
	}

	psBase->eType = eType;

	eError = gpsHandleFuncs->pfnCreateHandleBase(&psBase->psImplBase);
	if (eError != PVRSRV_OK)
	{
		goto ErrorFreeHandleBase;
	}

	psBase->psHashTab = HASH_Create_Extended(HANDLE_HASH_TAB_INIT_SIZE,
						 sizeof(HAND_KEY),
						 HASH_Func_Default,
						 HASH_Key_Comp_Default);
	if (psBase->psHashTab == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "PVRSRVAllocHandleBase: Couldn't create data pointer hash table"));
		eError = PVRSRV_ERROR_UNABLE_TO_CREATE_HASH_TABLE;
		goto ErrorDestroyHandleBase;
	}

	*ppsBase = psBase;

	UnlockHandle();

	return PVRSRV_OK;

ErrorDestroyHandleBase:
	(void)gpsHandleFuncs->pfnDestroyHandleBase(psBase->psImplBase);

ErrorFreeHandleBase:
	OSFreeMem(psBase);

ErrorUnlock:
	UnlockHandle();

	return eError;
}

#if defined(DEBUG)
typedef struct _COUNT_HANDLE_DATA_
{
	PVRSRV_HANDLE_BASE *psBase;
	IMG_UINT32 uiHandleDataCount;
} COUNT_HANDLE_DATA;

/* Used to count the number of handles that have data associated with them */
static PVRSRV_ERROR CountHandleDataWrapper(IMG_HANDLE hHandle, void *pvData)
{
	COUNT_HANDLE_DATA *psData = (COUNT_HANDLE_DATA *)pvData;
	HANDLE_DATA *psHandleData = NULL;
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsHandleFuncs);

	if (psData == NULL ||
	    psData->psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "CountHandleDataWrapper: Missing free data"));
		return PVRSRV_ERROR_INVALID_PARAMS;
	}

	eError = GetHandleData(psData->psBase,
			       &psHandleData,
			       hHandle,
			       PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "CountHandleDataWrapper: Couldn't get handle data for handle"));
		return eError;
	}

	if (psHandleData != NULL)
	{
		psData->uiHandleDataCount++;
	}

	return PVRSRV_OK;
}

/* Print a handle in the handle base. Used with the iterator callback. */
static PVRSRV_ERROR ListHandlesInBase(IMG_HANDLE hHandle, void *pvData)
{
	PVRSRV_HANDLE_BASE *psBase = (PVRSRV_HANDLE_BASE*) pvData;
	HANDLE_DATA *psHandleData = NULL;
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsHandleFuncs);

	if (psBase == NULL)
	{
		PVR_DPF((PVR_DBG_ERROR, "%s: Missing base", __func__));
		return PVRSRV_ERROR_INVALID_PARAMS;
	}

	eError = GetHandleData(psBase,
			       &psHandleData,
			       hHandle,
			       PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "%s: Couldn't get handle data for handle", __func__));
		return eError;
	}

	if (psHandleData != NULL)
	{
		PVR_DPF((PVR_DBG_WARNING, "    Handle: %6u, Refs: %3u, Type: %s (%u)",
				(IMG_UINT32) (uintptr_t) psHandleData->hHandle,
				psHandleData->ui32RefCount,
				HandleTypeToString(psHandleData->eType),
				psHandleData->eType));
	}

	return PVRSRV_OK;
}



#endif /* defined(DEBUG) */

typedef struct FREE_HANDLE_DATA_TAG
{
	PVRSRV_HANDLE_BASE *psBase;
	PVRSRV_HANDLE_TYPE eHandleFreeType;
	/* timing data (ns) to release bridge lock upon the deadline */
	IMG_UINT64 ui64TimeStart;
	IMG_UINT64 ui64MaxBridgeTime;
} FREE_HANDLE_DATA;

typedef struct FREE_KERNEL_HANDLE_DATA_TAG
{
	PVRSRV_HANDLE_BASE *psBase;
	HANDLE_DATA *psProcessHandleData;
	IMG_HANDLE hKernelHandle;
} FREE_KERNEL_HANDLE_DATA;

static INLINE IMG_BOOL _CheckIfMaxTimeExpired(IMG_UINT64 ui64TimeStart, IMG_UINT64 ui64MaxBridgeTime)
{
	IMG_UINT64 ui64Diff;
	IMG_UINT64 ui64Now = OSClockns64();

	if (ui64Now >= ui64TimeStart)
	{
		ui64Diff = ui64Now - ui64TimeStart;
	}
	else
	{
		/* time has wrapped around */
		ui64Diff = (UINT64_MAX - ui64TimeStart) + ui64Now;
	}

	return ui64Diff >= ui64MaxBridgeTime;
}

static PVRSRV_ERROR FreeKernelHandlesWrapperIterKernel(IMG_HANDLE hHandle, void *pvData)
{
	FREE_KERNEL_HANDLE_DATA *psData = (FREE_KERNEL_HANDLE_DATA *)pvData;
	HANDLE_DATA *psKernelHandleData = NULL;
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsHandleFuncs);

	/* Get kernel handle data. */
	eError = GetHandleData(KERNEL_HANDLE_BASE,
			    &psKernelHandleData,
			    hHandle,
			    PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "FreeKernelHandlesWrapperIterKernel: Couldn't get handle data for kernel handle"));
		return eError;
	}

	if (psKernelHandleData->pvData == psData->psProcessHandleData->pvData)
	{
		/* This kernel handle belongs to our process handle. */
		psData->hKernelHandle = hHandle;
	}

	return PVRSRV_OK;
}

static PVRSRV_ERROR FreeKernelHandlesWrapperIterProcess(IMG_HANDLE hHandle, void *pvData)
{
	FREE_KERNEL_HANDLE_DATA *psData = (FREE_KERNEL_HANDLE_DATA *)pvData;
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsHandleFuncs);

	/* Get process handle data. */
	eError = GetHandleData(psData->psBase,
			    &psData->psProcessHandleData,
			    hHandle,
			    PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "FreeKernelHandlesWrapperIterProcess: Couldn't get handle data for process handle"));
		return eError;
	}

	if ((psData->psProcessHandleData->eFlag == PVRSRV_HANDLE_ALLOC_FLAG_MULTI)
#if defined(SUPPORT_INSECURE_EXPORT)
		|| (psData->psProcessHandleData->eType == PVRSRV_HANDLE_TYPE_PVRSRV_FENCE_EXPORT)
#endif
		)
	{
		/* Only multi alloc process handles might be in kernel handle base. */
		psData->hKernelHandle = NULL;
		/* Iterate over kernel handles. */
		eError = gpsHandleFuncs->pfnIterateOverHandles(KERNEL_HANDLE_BASE->psImplBase,
									&FreeKernelHandlesWrapperIterKernel,
									(void *)psData);
		if (eError != PVRSRV_OK)
		{
			PVR_DPF((PVR_DBG_ERROR, "FreeKernelHandlesWrapperIterProcess: Failed to iterate over kernel handles"));
			return eError;
		}

		if (psData->hKernelHandle)
		{
			/* Release kernel handle which belongs to our process handle. */
			eError = gpsHandleFuncs->pfnReleaseHandle(KERNEL_HANDLE_BASE->psImplBase,
						psData->hKernelHandle,
						NULL);
			if (eError != PVRSRV_OK)
			{
				PVR_DPF((PVR_DBG_ERROR, "FreeKernelHandlesWrapperIterProcess: Couldn't release kernel handle"));
				return eError;
			}
		}
	}

	return PVRSRV_OK;
}

static PVRSRV_ERROR FreeHandleDataWrapper(IMG_HANDLE hHandle, void *pvData)
{
	FREE_HANDLE_DATA *psData = (FREE_HANDLE_DATA *)pvData;
	HANDLE_DATA *psHandleData = NULL;
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsHandleFuncs);

	if (psData == NULL ||
	    psData->psBase == NULL ||
	    psData->eHandleFreeType == PVRSRV_HANDLE_TYPE_NONE)
	{
		PVR_DPF((PVR_DBG_ERROR, "FreeHandleDataWrapper: Missing free data"));
		return PVRSRV_ERROR_INVALID_PARAMS;
	}

	eError = GetHandleData(psData->psBase,
			       &psHandleData,
			       hHandle,
			       PVRSRV_HANDLE_TYPE_NONE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR, "FreeHandleDataWrapper: Couldn't get handle data for handle"));
		return eError;
	}

	if (psHandleData == NULL || psHandleData->eType != psData->eHandleFreeType)
	{
		return PVRSRV_OK;
	}

	PVR_ASSERT(psHandleData->ui32RefCount > 0);

	while (psHandleData->ui32RefCount != 0)
	{
		if (psHandleData->pfnReleaseData != NULL)
		{
			eError = psHandleData->pfnReleaseData(psHandleData->pvData);
			if (eError == PVRSRV_ERROR_RETRY)
			{
				PVR_DPF((PVR_DBG_MESSAGE,
					 "FreeHandleDataWrapper: "
					 "Got retry while calling release data callback for %p (type = %d)",
					 hHandle,
					 (IMG_UINT32)psHandleData->eType));

				return eError;
			}
			else if (eError != PVRSRV_OK)
			{
				return eError;
			}
		}

		_HandleUnref(psHandleData);
	}

	if (!TEST_ALLOC_FLAG(psHandleData, PVRSRV_HANDLE_ALLOC_FLAG_MULTI))
	{
		HAND_KEY aKey;
		IMG_HANDLE hRemovedHandle;

		InitKey(aKey,
			psData->psBase,
			psHandleData->pvData,
			psHandleData->eType,
			ParentIfPrivate(psHandleData));

		hRemovedHandle = (IMG_HANDLE)HASH_Remove_Extended(psData->psBase->psHashTab, aKey);

		PVR_ASSERT(hRemovedHandle != NULL);
		PVR_ASSERT(hRemovedHandle == psHandleData->hHandle);
		PVR_UNREFERENCED_PARAMETER(hRemovedHandle);
	}

	eError = gpsHandleFuncs->pfnSetHandleData(psData->psBase->psImplBase, hHandle, NULL);
	if (eError != PVRSRV_OK)
	{
		return eError;
	}

	OSFreeMem(psHandleData);

	/* If we reach the end of the time slice release we can release the global
	 * lock, invoke the scheduler and reacquire the lock */
	if ((psData->ui64MaxBridgeTime != 0) && _CheckIfMaxTimeExpired(psData->ui64TimeStart, psData->ui64MaxBridgeTime))
	{
		PVR_DPF((PVR_DBG_MESSAGE, "FreeResourceByCriteria: Lock timeout (timeout: %" IMG_UINT64_FMTSPEC")",
								            psData->ui64MaxBridgeTime));
		UnlockHandle();
#if defined(PVRSRV_USE_BRIDGE_LOCK)
		OSReleaseBridgeLock();
#endif
		/* Invoke the scheduler to check if other processes are waiting for the lock */
		OSReleaseThreadQuanta();
#if defined(PVRSRV_USE_BRIDGE_LOCK)
		OSAcquireBridgeLock();
#endif
		LockHandle();
		/* Set again lock timeout and reset the counter */
		psData->ui64TimeStart = OSClockns64();
		PVR_DPF((PVR_DBG_MESSAGE, "FreeResourceByCriteria: Lock acquired again"));
	}

	return PVRSRV_OK;
}

static PVRSRV_HANDLE_TYPE g_aeOrderedFreeList[] =
{
	PVRSRV_HANDLE_TYPE_EVENT_OBJECT_CONNECT,
	PVRSRV_HANDLE_TYPE_SHARED_EVENT_OBJECT,
	PVRSRV_HANDLE_TYPE_RGX_FW_MEMDESC,
	PVRSRV_HANDLE_TYPE_RGX_RTDATA_CLEANUP,
	PVRSRV_HANDLE_TYPE_RGX_FREELIST,
	PVRSRV_HANDLE_TYPE_RGX_RPM_FREELIST,
	PVRSRV_HANDLE_TYPE_RGX_SERVER_RPM_CONTEXT,
	PVRSRV_HANDLE_TYPE_RGX_MEMORY_BLOCK,
	PVRSRV_HANDLE_TYPE_RGX_POPULATION,
	PVRSRV_HANDLE_TYPE_RGX_FWIF_ZSBUFFER,
	PVRSRV_HANDLE_TYPE_RGX_FWIF_RENDERTARGET,
	PVRSRV_HANDLE_TYPE_RGX_SERVER_RENDER_CONTEXT,
	PVRSRV_HANDLE_TYPE_RGX_SERVER_TQ_CONTEXT,
	PVRSRV_HANDLE_TYPE_RGX_SERVER_TQ_TDM_CONTEXT,
	PVRSRV_HANDLE_TYPE_RGX_SERVER_COMPUTE_CONTEXT,
	PVRSRV_HANDLE_TYPE_RGX_SERVER_RAY_CONTEXT,
	PVRSRV_HANDLE_TYPE_RGX_SERVER_KICKSYNC_CONTEXT,
	PVRSRV_HANDLE_TYPE_RI_HANDLE,
	PVRSRV_HANDLE_TYPE_SYNC_RECORD_HANDLE,
	PVRSRV_HANDLE_TYPE_SERVER_OP_COOKIE,
	PVRSRV_HANDLE_TYPE_SERVER_SYNC_PRIMITIVE,
	PVRSRV_HANDLE_TYPE_SERVER_SYNC_EXPORT,
	PVRSRV_HANDLE_TYPE_SYNC_PRIMITIVE_BLOCK,
	PVRSRV_HANDLE_TYPE_PVRSRV_TIMELINE_SERVER,
	PVRSRV_HANDLE_TYPE_PVRSRV_FENCE_EXPORT,
	PVRSRV_HANDLE_TYPE_PVRSRV_FENCE_SERVER,
	PVRSRV_HANDLE_TYPE_DEVMEMINT_MAPPING,
	PVRSRV_HANDLE_TYPE_DEVMEMINT_RESERVATION,
	PVRSRV_HANDLE_TYPE_DEVMEMINT_HEAP,
	PVRSRV_HANDLE_TYPE_DEVMEMINT_CTX_EXPORT,
	PVRSRV_HANDLE_TYPE_DEV_PRIV_DATA,
	PVRSRV_HANDLE_TYPE_DEVMEMINT_CTX,
	PVRSRV_HANDLE_TYPE_PHYSMEM_PMR_PAGELIST,
	PVRSRV_HANDLE_TYPE_PHYSMEM_PMR_SECURE_EXPORT,
	PVRSRV_HANDLE_TYPE_PHYSMEM_PMR_EXPORT,
	PVRSRV_HANDLE_TYPE_PHYSMEM_PMR,
	PVRSRV_HANDLE_TYPE_DEVMEM_MEM_IMPORT,
	PVRSRV_HANDLE_TYPE_PMR_LOCAL_EXPORT_HANDLE,
	PVRSRV_HANDLE_TYPE_DC_PIN_HANDLE,
	PVRSRV_HANDLE_TYPE_DC_BUFFER,
	PVRSRV_HANDLE_TYPE_DC_DISPLAY_CONTEXT,
	PVRSRV_HANDLE_TYPE_DC_DEVICE,
	PVRSRV_HANDLE_TYPE_PVR_TL_SD,
	PVRSRV_HANDLE_TYPE_MM_PLAT_CLEANUP
};

/*!
******************************************************************************

 @Function	PVRSRVFreeKernelHandles

 @Description	Free kernel handles which belongs to process handles

 @Input 	psBase - pointer to handle base structure

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVFreeKernelHandles(PVRSRV_HANDLE_BASE *psBase)
{
	FREE_KERNEL_HANDLE_DATA sHandleData = { };
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsHandleFuncs);

	LockHandle();

	sHandleData.psBase = psBase;
	/* Iterate over process handles. */
	eError = gpsHandleFuncs->pfnIterateOverHandles(psBase->psImplBase,
								&FreeKernelHandlesWrapperIterProcess,
								(void *)&sHandleData);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVFreeKernelHandles: Failed to iterate over handles (%s)",
			 PVRSRVGetErrorStringKM(eError)));
		goto ExitUnlock;
	}

	eError = PVRSRV_OK;

ExitUnlock:
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVFreeHandleBase

 @Description	Free a handle base structure

 @Input 	psBase - pointer to handle base structure

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVFreeHandleBase(PVRSRV_HANDLE_BASE *psBase, IMG_UINT64 ui64MaxBridgeTime)
{
#if defined(DEBUG)
	COUNT_HANDLE_DATA sCountData = { };
#endif
	FREE_HANDLE_DATA sHandleData = { };
	IMG_UINT32 i;
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsHandleFuncs);

	LockHandle();

	sHandleData.psBase = psBase;
	sHandleData.ui64TimeStart = OSClockns64();
	sHandleData.ui64MaxBridgeTime = ui64MaxBridgeTime;


#if defined(DEBUG)

	sCountData.psBase = psBase;

	eError = gpsHandleFuncs->pfnIterateOverHandles(psBase->psImplBase,
						       &CountHandleDataWrapper,
						       (void *)&sCountData);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVFreeHandleBase: Failed to perform handle count (%s)",
			 PVRSRVGetErrorStringKM(eError)));
		goto ExitUnlock;
	}

	if (sCountData.uiHandleDataCount != 0)
	{
		IMG_BOOL bList = sCountData.uiHandleDataCount < HANDLE_DEBUG_LISTING_MAX_NUM;

		PVR_DPF((PVR_DBG_WARNING,
			 "%s: %u remaining handles in handle base 0x%p "
			 "(PVRSRV_HANDLE_BASE_TYPE %u).%s",
			 __func__,
			 sCountData.uiHandleDataCount,
			 psBase,
			 psBase->eType,
			 bList ? "": " Skipping details, too many items..."));

		if (bList)
		{
			PVR_DPF((PVR_DBG_WARNING, "-------- Listing Handles --------"));
			eError = gpsHandleFuncs->pfnIterateOverHandles(psBase->psImplBase,
										   &ListHandlesInBase,
										   psBase);
			PVR_DPF((PVR_DBG_WARNING, "-------- Done Listing    --------"));
		}
	}

#endif /* defined(DEBUG) */

	/*
	 * As we're freeing handles based on type, make sure all
	 * handles have actually had their data freed to avoid
	 * resources being leaked
	 */
	for (i = 0; i < ARRAY_SIZE(g_aeOrderedFreeList); i++)
	{
		sHandleData.eHandleFreeType = g_aeOrderedFreeList[i];

		/* Make sure all handles have been freed before destroying the handle base */
		eError = gpsHandleFuncs->pfnIterateOverHandles(psBase->psImplBase,
							       &FreeHandleDataWrapper,
							       (void *)&sHandleData);
		if (eError != PVRSRV_OK)
		{
			goto ExitUnlock;
		}
	}


	if (psBase->psHashTab != NULL)
	{
		HASH_Delete(psBase->psHashTab);
	}

	eError = gpsHandleFuncs->pfnDestroyHandleBase(psBase->psImplBase);
	if (eError != PVRSRV_OK)
	{
		goto ExitUnlock;
	}

	OSFreeMem(psBase);

	eError = PVRSRV_OK;

ExitUnlock:
	UnlockHandle();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVHandleInit

 @Description	Initialise handle management

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVHandleInit(void)
{
	PVRSRV_ERROR eError;

	PVR_ASSERT(gpsKernelHandleBase == NULL);
	PVR_ASSERT(gpsHandleFuncs == NULL);
	PVR_ASSERT(!gbLockInitialised);

	eError = OSLockCreate(&gHandleLock, LOCK_TYPE_PASSIVE);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVHandleInit: Creation of handle global lock failed (%s)",
			 PVRSRVGetErrorStringKM(eError)));
		return eError;
	}
	gbLockInitialised = IMG_TRUE;

	eError = PVRSRVHandleGetFuncTable(&gpsHandleFuncs);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVHandleInit: PVRSRVHandleGetFuncTable failed (%s)",
			 PVRSRVGetErrorStringKM(eError)));
		goto ErrorHandleDeinit;
	}

	eError = PVRSRVAllocHandleBase(&gpsKernelHandleBase,
	                               PVRSRV_HANDLE_BASE_TYPE_GLOBAL);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVHandleInit: PVRSRVAllocHandleBase failed (%s)",
			 PVRSRVGetErrorStringKM(eError)));
		goto ErrorHandleDeinit;
	}

	eError = gpsHandleFuncs->pfnEnableHandlePurging(gpsKernelHandleBase->psImplBase);
	if (eError != PVRSRV_OK)
	{
		PVR_DPF((PVR_DBG_ERROR,
			 "PVRSRVHandleInit: PVRSRVEnableHandlePurging failed (%s)",
			 PVRSRVGetErrorStringKM(eError)));
		goto ErrorHandleDeinit;
	}

	return PVRSRV_OK;

ErrorHandleDeinit:
	(void) PVRSRVHandleDeInit();

	return eError;
}

/*!
******************************************************************************

 @Function	PVRSRVHandleDeInit

 @Description	De-initialise handle management

 @Return	Error code or PVRSRV_OK

******************************************************************************/
PVRSRV_ERROR PVRSRVHandleDeInit(void)
{
	PVRSRV_ERROR eError = PVRSRV_OK;

	if (gpsHandleFuncs != NULL)
	{
		if (gpsKernelHandleBase != NULL)
		{
			eError = PVRSRVFreeHandleBase(gpsKernelHandleBase, 0 /* do not release bridge lock */);
			if (eError == PVRSRV_OK)
			{
				gpsKernelHandleBase = NULL;
			}
			else
			{
				PVR_DPF((PVR_DBG_ERROR,
					 "PVRSRVHandleDeInit: FreeHandleBase failed (%s)",
					 PVRSRVGetErrorStringKM(eError)));
			}
		}

		if (eError == PVRSRV_OK)
		{
			gpsHandleFuncs = NULL;
		}
	}
	else
	{
		/* If we don't have a handle function table we shouldn't have a handle base either */
		PVR_ASSERT(gpsKernelHandleBase == NULL);
	}

	if (gbLockInitialised)
	{
		OSLockDestroy(gHandleLock);
		gbLockInitialised = IMG_FALSE;
	}

	return eError;
}
