blob: f7ff7515edfbce864636e69f25a6c5ad4bb16b68 [file] [log] [blame]
/*************************************************************************/ /*!
@File
@Title KM server Transport Layer implementation
@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
@Description Main bridge APIs for Transport Layer client functions
@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.
*/ /**************************************************************************/
#include <stddef.h>
#include "img_defs.h"
//#define PVR_DPF_FUNCTION_TRACE_ON 1
#undef PVR_DPF_FUNCTION_TRACE_ON
#include "pvr_debug.h"
#include "connection_server.h"
#include "allocmem.h"
#include "devicemem.h"
#include "tlintern.h"
#include "tlstream.h"
#include "tlserver.h"
#define NO_STREAM_WAIT_PERIOD 2000000ULL
#define NO_DATA_WAIT_PERIOD 1000000ULL
#define NO_ACQUIRE 0xffffffffU
#include "rgxhwperf.h"
/*
* Transport Layer Client API Kernel-Mode bridge implementation
*/
PVRSRV_ERROR
TLServerOpenStreamKM(const IMG_CHAR* pszName,
IMG_UINT32 ui32Mode,
PTL_STREAM_DESC* ppsSD,
PMR** ppsTLPMR)
{
PVRSRV_ERROR eError = PVRSRV_OK;
PVRSRV_ERROR eErrorEO = PVRSRV_OK;
PTL_SNODE psNode = 0;
TL_STREAM_DESC* psNewSD = 0;
IMG_HANDLE hEvent;
IMG_BOOL bIsWriteOnly = ui32Mode & PVRSRV_STREAM_FLAG_OPEN_WO ?
IMG_TRUE : IMG_FALSE;
PTL_GLOBAL_DATA psGD = TLGGD();
#if defined(PVR_DPF_FUNCTION_TRACE_ON)
PVR_DPF((PVR_DBG_CALLTRACE, "--> %s:%d entered (%s, %x)", __func__, __LINE__, pszName, ui32Mode));
#endif
PVR_ASSERT(pszName);
/* Acquire TL_GLOBAL_DATA lock here, as if the following TLFindStreamNodeByName
* returns NON NULL PTL_SNODE, we try updating the global data client count and
* PTL_SNODE's psRDesc and we want to make sure the TL_SNODE is valid (eg. has
* not been deleted) while we are updating it
*/
OSLockAcquire (psGD->hTLGDLock);
psNode = TLFindStreamNodeByName(pszName);
if ((psNode == NULL) && (ui32Mode & PVRSRV_STREAM_FLAG_OPEN_WAIT))
{ /* Blocking code to wait for stream to be created if it does not exist */
eError = OSEventObjectOpen(psGD->hTLEventObj, &hEvent);
PVR_LOGG_IF_ERROR (eError, "OSEventObjectOpen", e0);
do
{
if ((psNode = TLFindStreamNodeByName(pszName)) == NULL)
{
PVR_DPF((PVR_DBG_MESSAGE, "Stream %s does not exist, waiting...", pszName));
/* Release TL_GLOBAL_DATA lock before sleeping */
OSLockRelease (psGD->hTLGDLock);
/* Will exit OK or with timeout, both cases safe to ignore */
eErrorEO = OSEventObjectWaitTimeout(hEvent, NO_STREAM_WAIT_PERIOD);
/* Acquire lock after waking up */
OSLockAcquire (psGD->hTLGDLock);
}
}
while ((psNode == NULL) && (eErrorEO == PVRSRV_OK));
eError = OSEventObjectClose(hEvent);
PVR_LOGG_IF_ERROR (eError, "OSEventObjectClose", e0);
}
/* Make sure we have found a stream node after wait/search */
if (psNode == NULL)
{
/* Did we exit the wait with timeout, inform caller */
if (eErrorEO == PVRSRV_ERROR_TIMEOUT)
{
eError = eErrorEO;
}
else
{
eError = PVRSRV_ERROR_NOT_FOUND;
PVR_DPF((PVR_DBG_ERROR, "Stream \"%s\" does not exist", pszName));
}
goto e0;
}
/* Allocate memory for the stream. The memory will be allocated with the
* first call. */
eError = TLAllocSharedMemIfNull(psNode->psStream);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "Failed to allocate memory for stream"
" \"%s\"", pszName));
return eError;
}
if (bIsWriteOnly)
{
/* If psWDesc == NULL it means that this is the first attempt
* to open stream for write. If yes create the descriptor or increment
* reference count otherwise. */
if (psNode->psWDesc == NULL)
{
psNewSD = TLMakeStreamDesc(psNode, ui32Mode, NULL);
psNode->psWDesc = psNewSD;
}
else
{
psNewSD = psNode->psWDesc;
psNode->psWDesc->uiRefCount++;
}
if (!psNewSD)
{
PVR_DPF((PVR_DBG_ERROR, "Not possible to make a new stream"
" writer descriptor"));
eError = PVRSRV_ERROR_OUT_OF_MEMORY;
goto e1;
}
psNode->uiWRefCount++;
}
else
{
// Only one reader per stream supported
if (psNode->psRDesc != NULL)
{
PVR_DPF((PVR_DBG_ERROR, "Cannot open \"%s\" stream, stream already"
" opened", pszName));
eError = PVRSRV_ERROR_ALREADY_OPEN;
goto e0;
}
// Create an event handle for this client to wait on when no data in
// stream buffer.
eError = OSEventObjectOpen(psNode->hReadEventObj, &hEvent);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "Not possible to open node's event object"));
eError = PVRSRV_ERROR_UNABLE_TO_CREATE_EVENT;
goto e0;
}
psNewSD = TLMakeStreamDesc(psNode, ui32Mode, hEvent);
psNode->psRDesc = psNewSD;
if (!psNewSD)
{
PVR_DPF((PVR_DBG_ERROR, "Not possible to make a new stream descriptor"));
eError = PVRSRV_ERROR_OUT_OF_MEMORY;
goto e1;
}
PVR_DPF((PVR_DBG_VERBOSE,
"TLServerOpenStreamKM evList=%p, evObj=%p",
psNode->hReadEventObj,
psNode->psRDesc->hReadEvent));
}
// Copy the import handle back to the user mode API to enable access to
// the stream buffer from user-mode process.
eError = DevmemLocalGetImportHandle(TLStreamGetBufferPointer(psNode->psStream), (void**) ppsTLPMR);
PVR_LOGG_IF_ERROR(eError, "DevmemLocalGetImportHandle", e2);
psGD->uiClientCnt++;
/* Global data updated. Now release global lock */
OSLockRelease (psGD->hTLGDLock);
*ppsSD = psNewSD;
/* This callback is executed only on reader open. There are some actions
* executed on reader open that don't make much sense for writers e.g.
* injection on time synchronisation packet into the stream. */
if (!bIsWriteOnly && psNode->psStream->pfOnReaderOpenCallback != NULL)
{
psNode->psStream->pfOnReaderOpenCallback(
psNode->psStream->pvOnReaderOpenUserData);
}
if (bIsWriteOnly)
{
/* Sending HWPerf event from TL is a temporary solution and this
* will change once TL is expanded by event allowing to signal
* stream opening. */
RGX_HWPERF_HOST_CTRL(CLIENT_STREAM_OPEN,
OSGetCurrentClientProcessIDKM());
}
PVR_DPF((PVR_DBG_MESSAGE, "%s: Stream %s opened for %s", __func__, pszName,
ui32Mode & PVRSRV_STREAM_FLAG_OPEN_WO ? "write" : "read"));
PVR_DPF_RETURN_OK;
e2:
OSFreeMem(psNewSD);
e1:
OSEventObjectClose(hEvent);
e0:
OSLockRelease (psGD->hTLGDLock);
PVR_DPF_RETURN_RC (eError);
}
PVRSRV_ERROR
TLServerCloseStreamKM(PTL_STREAM_DESC psSD)
{
PVRSRV_ERROR eError = PVRSRV_OK;
PTL_GLOBAL_DATA psGD = TLGGD();
PTL_SNODE psNode = 0;
PTL_STREAM psStream;
IMG_BOOL bDestroyStream;
IMG_BOOL bIsWriteOnly = psSD->ui32Flags & PVRSRV_STREAM_FLAG_OPEN_WO ?
IMG_TRUE : IMG_FALSE;
PVR_DPF_ENTERED;
PVR_ASSERT(psSD);
// Sanity check, quick exit if there are no streams
if (psGD->psHead == NULL)
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_HANDLE_NOT_FOUND);
}
// Check stream still valid
psNode = TLFindStreamNodeByDesc(psSD);
if ((psNode == NULL) || (psNode != psSD->psNode))
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_HANDLE_NOT_FOUND);
}
/* Since the descriptor is valid, the stream should not have been made NULL */
PVR_ASSERT (psNode->psStream);
/* Save the stream's reference in-case its destruction is required after this
* client is removed */
psStream = psNode->psStream;
/* Acquire TL_GLOBAL_DATA lock as the following TLRemoveDescAndTryFreeStreamNode
* call will update the TL_SNODE's descriptor value */
OSLockAcquire (psGD->hTLGDLock);
/* Close event handle because event object list might be destroyed in
* TLUnrefDescAndTryFreeStreamNode(). */
if (!bIsWriteOnly)
{
// Close and free the event handle resource used by this descriptor
eError = OSEventObjectClose(psSD->hReadEvent);
if (eError != PVRSRV_OK)
{
// Log error but continue as it seems best
PVR_DPF((PVR_DBG_ERROR, "OSEventObjectClose() failed error %d",
eError));
eError = PVRSRV_ERROR_UNABLE_TO_DESTROY_EVENT;
}
}
else
{
/* Sending HWPerf event from TL is a temporary solution and this
* will change once TL is expanded by event allowing to signal
* stream closing. */
RGX_HWPERF_HOST_CTRL(CLIENT_STREAM_CLOSE,
OSGetCurrentClientProcessIDKM());
}
// Remove descriptor from stream object/list
bDestroyStream = TLUnrefDescAndTryFreeStreamNode (psNode, psSD);
// Assert the counter is sane after input data validated.
PVR_ASSERT(psGD->uiClientCnt > 0);
psGD->uiClientCnt--;
OSLockRelease (psGD->hTLGDLock);
/* Destroy the stream if its TL_SNODE was removed from TL_GLOBAL_DATA */
if (bDestroyStream)
{
TLStreamDestroy (psStream);
psStream = NULL;
}
PVR_DPF((PVR_DBG_VERBOSE, "%s: Stream closed", __func__));
/* Free the descriptor if ref count reaches 0. */
if (psSD->uiRefCount == 0)
{
// Free the stream descriptor object
OSFreeMem(psSD);
}
PVR_DPF_RETURN_RC(eError);
}
PVRSRV_ERROR
TLServerReserveStreamKM(PTL_STREAM_DESC psSD,
IMG_UINT32* ui32BufferOffset,
IMG_UINT32 ui32Size,
IMG_UINT32 ui32SizeMin,
IMG_UINT32* pui32Available)
{
TL_GLOBAL_DATA* psGD = TLGGD();
PTL_SNODE psNode = 0;
IMG_UINT8* pui8Buffer = NULL;
PVRSRV_ERROR eError;
PVR_DPF_ENTERED;
PVR_ASSERT(psSD);
if (!(psSD->ui32Flags & PVRSRV_STREAM_FLAG_OPEN_WO))
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS);
}
// Sanity check, quick exit if there are no streams
if (psGD->psHead == NULL)
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_ERROR);
}
/* Acquire the global lock. We have to be sure that no one modifies
* the list while we are looking for our stream. */
OSLockAcquire(psGD->hTLGDLock);
// Check stream still valid
psNode = TLFindAndGetStreamNodeByDesc(psSD);
OSLockRelease(psGD->hTLGDLock);
if ((psNode == NULL) || (psNode != psSD->psNode))
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_HANDLE_NOT_FOUND);
}
/* Since we have a valid stream descriptor, the stream should not have been
* made NULL by any producer context. */
PVR_ASSERT (psNode->psStream);
eError = TLStreamReserve2(psNode->psStream, &pui8Buffer, ui32Size,
ui32SizeMin, pui32Available);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_WARNING, "Failed to reserve the stream (%d).", eError));
}
else if (pui8Buffer == NULL)
{
PVR_DPF((PVR_DBG_WARNING, "Not enough space in the stream."));
eError = PVRSRV_ERROR_STREAM_RESERVE_TOO_BIG;
}
else
{
*ui32BufferOffset = pui8Buffer - psNode->psStream->pbyBuffer;
PVR_ASSERT(*ui32BufferOffset < psNode->psStream->ui32Size);
}
OSLockAcquire(psGD->hTLGDLock);
TLReturnStreamNode(psNode);
OSLockRelease(psGD->hTLGDLock);
PVR_DPF_RETURN_RC(eError);
}
PVRSRV_ERROR
TLServerCommitStreamKM(PTL_STREAM_DESC psSD,
IMG_UINT32 ui32Size)
{
TL_GLOBAL_DATA* psGD = TLGGD();
PTL_SNODE psNode = 0;
PVRSRV_ERROR eError;
PVR_DPF_ENTERED;
PVR_ASSERT(psSD);
if (!(psSD->ui32Flags & PVRSRV_STREAM_FLAG_OPEN_WO))
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS);
}
// Sanity check, quick exit if there are no streams
if (psGD->psHead == NULL)
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_ERROR);
}
/* Acquire the global lock. We have to be sure that no one modifies
* the list while we are looking for our stream. */
OSLockAcquire(psGD->hTLGDLock);
// Check stream still valid
psNode = TLFindAndGetStreamNodeByDesc(psSD);
OSLockRelease(psGD->hTLGDLock);
if ((psNode == NULL) || (psNode != psSD->psNode))
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_HANDLE_NOT_FOUND);
}
/* Since we have a valid stream descriptor, the stream should not have been
* made NULL by any producer context. */
PVR_ASSERT (psNode->psStream);
eError = TLStreamCommit(psNode->psStream, ui32Size);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "Failed to commit data into stream."));
}
OSLockAcquire(psGD->hTLGDLock);
TLReturnStreamNode(psNode);
OSLockRelease(psGD->hTLGDLock);
PVR_DPF_RETURN_RC(eError);
}
PVRSRV_ERROR
TLServerDiscoverStreamsKM(const IMG_CHAR *pszNamePattern,
IMG_UINT32 ui32Max,
IMG_UINT32 *pui32Streams,
IMG_UINT32 *pui32NumFound)
{
if (*pszNamePattern == '\0')
return PVRSRV_ERROR_INVALID_PARAMS;
// Sanity check, quick exit if there are no streams
if (TLGGD()->psHead == NULL)
{
*pui32NumFound = 0;
return PVRSRV_OK;
}
OSLockAcquire(TLGGD()->hTLGDLock);
*pui32NumFound = TLDiscoverStreamNodes(pszNamePattern, pui32Streams,
ui32Max);
OSLockRelease(TLGGD()->hTLGDLock);
return PVRSRV_OK;
}
PVRSRV_ERROR
TLServerAcquireDataKM(PTL_STREAM_DESC psSD,
IMG_UINT32* puiReadOffset,
IMG_UINT32* puiReadLen)
{
PVRSRV_ERROR eError = PVRSRV_OK;
TL_GLOBAL_DATA* psGD = TLGGD();
IMG_UINT32 uiTmpOffset = NO_ACQUIRE;
IMG_UINT32 uiTmpLen = 0;
PTL_SNODE psNode = 0;
PVR_DPF_ENTERED;
PVR_ASSERT(psSD);
// Sanity check, quick exit if there are no streams
if (psGD->psHead == NULL)
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_ERROR);
}
// Check stream still valid
psNode = TLFindStreamNodeByDesc(psSD);
if ((psNode == NULL) || (psNode != psSD->psNode))
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_HANDLE_NOT_FOUND);
}
/* If we are here, the stream will never be made NULL until this context itself
* calls TLRemoveDescAndTryFreeStreamNode(). This is because the producer will
* fail to make the stream NULL (by calling TLTryRemoveStreamAndFreeStreamNode)
* when a valid stream descriptor is present (i.e. a client is connected).
* Hence, no checks for stream being NON NULL are required after this. */
PVR_ASSERT (psNode->psStream);
//PVR_DPF((PVR_DBG_VERBOSE, "TLServerAcquireDataKM evList=%p, evObj=%p", psSD->psNode->hReadEventObj, psSD->hReadEvent));
/* Check for data in the associated stream buffer, sleep/wait if none */
while (((uiTmpLen = TLStreamAcquireReadPos(psNode->psStream, &uiTmpOffset)) == 0) &&
(!(psSD->ui32Flags&PVRSRV_STREAM_FLAG_ACQUIRE_NONBLOCKING)) )
{
PVR_DPF((PVR_DBG_VERBOSE, "TLAcquireDataKM sleeping..."));
// Loop around if EndOfStream (nothing to read) and wait times out,
// exit loop if not time out but data is ready for client
while (TLStreamEOS(psNode->psStream))
{
eError = OSEventObjectWaitTimeout(psSD->hReadEvent, NO_DATA_WAIT_PERIOD);
if (eError != PVRSRV_OK)
{
/* Return timeout or other error condition to the caller who
* can choose to call again if desired. We don't block
* Indefinitely as we want the user mode application to have a
* chance to break out and end if it needs to, so we return the
* time out error code. */
PVR_DPF((PVR_DBG_VERBOSE, "TL Server timed out"));
PVR_DPF_RETURN_RC(eError);
}
else
{
PVR_DPF((PVR_DBG_VERBOSE, "TL Server signalled"));
}
}
}
/* Data available now if we reach here in blocking more or we take the
* values as is in non-blocking mode which might be all zeros. */
*puiReadOffset = uiTmpOffset;
*puiReadLen = uiTmpLen;
PVR_DPF((PVR_DBG_VERBOSE, "TLAcquireDataKM return offset=%d, len=%d bytes", *puiReadOffset, *puiReadLen));
PVR_DPF_RETURN_OK;
}
PVRSRV_ERROR
TLServerReleaseDataKM(PTL_STREAM_DESC psSD,
IMG_UINT32 uiReadOffset,
IMG_UINT32 uiReadLen)
{
TL_GLOBAL_DATA* psGD = TLGGD();
PTL_SNODE psNode = 0;
PVR_DPF_ENTERED;
/* Unreferenced in release builds */
PVR_UNREFERENCED_PARAMETER(uiReadOffset);
PVR_ASSERT(psSD);
// Sanity check, quick exit if there are no streams
if (psGD->psHead == NULL)
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_ERROR);
}
// Check stream still valid
psNode = TLFindStreamNodeByDesc(psSD);
if ((psNode == NULL) || (psNode != psSD->psNode))
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_HANDLE_NOT_FOUND);
}
/* Since we have a valid stream descriptor, the stream should not have been
* made NULL by any producer context. */
PVR_ASSERT (psNode->psStream);
PVR_DPF((PVR_DBG_VERBOSE, "TLReleaseDataKM uiReadOffset=%d, uiReadLen=%d", uiReadOffset, uiReadLen));
// Move read position on to free up space in stream buffer
TLStreamAdvanceReadPos(psNode->psStream, uiReadLen);
PVR_DPF_RETURN_OK;
}
PVRSRV_ERROR
TLServerWriteDataKM(PTL_STREAM_DESC psSD,
IMG_UINT32 ui32Size,
IMG_BYTE* pui8Data)
{
TL_GLOBAL_DATA* psGD = TLGGD();
PTL_SNODE psNode = 0;
PVRSRV_ERROR eError;
PVR_DPF_ENTERED;
PVR_ASSERT(psSD);
if (!(psSD->ui32Flags & PVRSRV_STREAM_FLAG_OPEN_WO))
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS);
}
// Sanity check, quick exit if there are no streams
if (psGD->psHead == NULL)
{
PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_ERROR);
}
OSLockAcquire(psGD->hTLGDLock);
// Check stream still valid
psNode = TLFindAndGetStreamNodeByDesc(psSD);
OSLockRelease(psGD->hTLGDLock);
if ((psNode == NULL) || (psNode != psSD->psNode))
{
OSLockRelease(psGD->hTLGDLock);
PVR_DPF_RETURN_RC(PVRSRV_ERROR_HANDLE_NOT_FOUND);
}
/* Since we have a valid stream descriptor, the stream should not have been
* made NULL by any producer context. */
PVR_ASSERT (psNode->psStream);
eError = TLStreamWrite(psNode->psStream, pui8Data, ui32Size);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "Failed to write data to the stream (%d).",
eError));
}
OSLockAcquire(psGD->hTLGDLock);
TLReturnStreamNode(psNode);
OSLockRelease(psGD->hTLGDLock);
PVR_DPF_RETURN_RC(eError);
}
/*****************************************************************************
End of file (tlserver.c)
*****************************************************************************/