/*
 * Internal stream APIs to server part of Transport Layer, declared in
 * header tlintern.h. Direct pointers to stream objects are used here as
 * these functions are internal.
 */
IMG_UINT32
TLStreamAcquireReadPos(PTL_STREAM psStream, IMG_UINT32* puiReadOffset)
{
	IMG_UINT32 uiReadLen = 0;
	IMG_UINT32 ui32LRead, ui32LWrite;

	PVR_DPF_ENTERED;

	PVR_ASSERT(psStream);
	PVR_ASSERT(puiReadOffset);

	/* Grab a local copy */
	ui32LRead = psStream->ui32Read;
	ui32LWrite = psStream->ui32Write;

	/* No data available and CB defined - try and get data */
	if ((ui32LRead == ui32LWrite) && psStream->pfProducerCallback)
	{
		PVRSRV_ERROR eRc;
		IMG_UINT32   ui32Resp = 0;

		eRc = ((TL_STREAM_SOURCECB)psStream->pfProducerCallback)(psStream, TL_SOURCECB_OP_CLIENT_EOS,
				&ui32Resp, psStream->pvProducerUserData);
		PVR_LOG_IF_ERROR(eRc, "TLStream->pfProducerCallback");

		ui32LWrite = psStream->ui32Write;
	}

	/* No data available... */
	if (ui32LRead == ui32LWrite)
	{
		PVR_DPF_RETURN_VAL(0);
	}

	/* Data is available to read... */
	*puiReadOffset = ui32LRead;

	/*PVR_DPF((PVR_DBG_VERBOSE,
	 *		"TLStreamAcquireReadPos Start before: Write:%d, Read:%d, size:%d",
	 *		ui32LWrite, ui32LRead, psStream->ui32Size));
	 */

	if ( ui32LRead > ui32LWrite )
	{	/* CB has wrapped around. 
		 * Return the first contiguous piece of memory, ie [ReadLen,EndOfBuffer]
		 * and let a subsequent AcquireReadPos read the rest of the Buffer */
		/*PVR_DPF((PVR_DBG_VERBOSE, "TLStreamAcquireReadPos buffer has wrapped"));*/
		uiReadLen = psStream->ui32Size - ui32LRead;
	}
	else
	{	// CB has not wrapped
		uiReadLen = ui32LWrite - ui32LRead;
	}

	PVR_DPF_RETURN_VAL(uiReadLen);
}
PVRSRV_ERROR
TLStreamOpen(IMG_HANDLE *phStream,
             IMG_CHAR   *szStreamName)
{
 	PTL_SNODE  psTmpSNode;

	PVR_DPF_ENTERED;

	if ( IMG_NULL == phStream || IMG_NULL == szStreamName )
	{
		PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS);
	}
	
	/* Search for a stream node with a matching stream name */
	psTmpSNode = TLFindStreamNodeByName(szStreamName);

	if ( IMG_NULL == psTmpSNode )
	{
		PVR_DPF_RETURN_RC(PVRSRV_ERROR_NOT_FOUND);
	}
	else
	{ /* Found a stream to open. lock and increase reference count */

//Thread Safety: Not yet implemented	OSLockAcquire(psTmpStream->hLock);
		psTmpSNode->psStream->uiRefCount++;
		*phStream = (IMG_HANDLE)psTmpSNode->psStream;
//Thread Safety: Not yet implemented	OSLockRelease(psTmpStream->hLock);

		PVR_DPF_RETURN_VAL(PVRSRV_OK);
	}
}
IMG_BOOL TLTryRemoveStreamAndFreeStreamNode(PTL_SNODE psRemove)
{
	PVR_DPF_ENTERED;

	PVR_ASSERT(psRemove);

	/* If there is a client connected to this stream, defer stream's deletion */
	if (psRemove->psRDesc != IMG_NULL)
	{
		PVR_DPF_RETURN_VAL (IMG_FALSE);
	}

	/* Remove stream from TL_GLOBAL_DATA's list and free stream node */	
	psRemove->psStream = IMG_NULL;
	RemoveAndFreeStreamNode(psRemove);

	PVR_DPF_RETURN_VAL (IMG_TRUE);
}
PTL_SNODE TLFindStreamNodeByDesc(PTL_STREAM_DESC psRDesc)
{
	TL_GLOBAL_DATA*  psGD = TLGGD();
	PTL_SNODE 		 psn;

	PVR_DPF_ENTERED;

	PVR_ASSERT(psRDesc);

	for (psn = psGD->psHead; psn; psn=psn->psNext)
	{
		if (psn->psRDesc == psRDesc)
		{
			PVR_DPF_RETURN_VAL(psn);
		}
	}
	PVR_DPF_RETURN_VAL(IMG_NULL);
}
DEVMEM_EXPORTCOOKIE*
TLStreamGetBufferCookie(PTL_STREAM psStream)
{
	PVR_DPF_ENTERED;

	PVR_ASSERT(psStream);

	PVR_DPF_RETURN_VAL(&psStream->sExportCookie);
}
PTL_SNODE TLFindStreamNodeByName(IMG_PCHAR pszName)
{
	TL_GLOBAL_DATA*  psGD = TLGGD();
	PTL_SNODE 		 psn;

	PVR_DPF_ENTERED;

	PVR_ASSERT(pszName);

	for (psn = psGD->psHead; psn; psn=psn->psNext)
	{
		if (psn->psStream && OSStringCompare(psn->psStream->szName, pszName)==0)
		{
			PVR_DPF_RETURN_VAL(psn);
		}
	}

	PVR_DPF_RETURN_VAL(IMG_NULL);
}
IMG_BOOL
TLStreamEOS(PTL_STREAM psStream)
{
	PVR_DPF_ENTERED;

	PVR_ASSERT(psStream);

	/* If both pointers are equal then the buffer is empty */
	PVR_DPF_RETURN_VAL( psStream->ui32Read == psStream->ui32Write );
}
IMG_BOOL TLRemoveDescAndTryFreeStreamNode(PTL_SNODE psRemove)
{
	PVR_DPF_ENTERED;

	PVR_ASSERT(psRemove);

	/* Remove stream descriptor (i.e. stream reader context) */
	psRemove->psRDesc = IMG_NULL;

	/* Do not Free Stream Node if there is a write reference (a producer context) to the stream */
	if (0 != psRemove->uiWRefCount)
	{
		PVR_DPF_RETURN_VAL (IMG_FALSE);
	}

	/* Make stream pointer NULL to prevent it from being destroyed in RemoveAndFreeStreamNode
	 * Cleanup of stream should be done by the calling context */
	psRemove->psStream = IMG_NULL;
	RemoveAndFreeStreamNode(psRemove);
	
	PVR_DPF_RETURN_VAL (IMG_TRUE);
}