PVRSRV_ERROR TLStreamWrite(IMG_HANDLE hStream, IMG_UINT8 *pui8Src, IMG_UINT32 ui32Size) { IMG_BYTE *pbyDest = IMG_NULL; PVRSRV_ERROR eError; PVR_DPF_ENTERED; if ( IMG_NULL == hStream ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS); } eError = TLStreamReserve(hStream, &pbyDest, ui32Size); if ( PVRSRV_OK != eError ) { PVR_DPF_RETURN_RC(eError); } else { PVR_ASSERT ( pbyDest != NULL ); OSMemCopy((IMG_VOID*)pbyDest, (IMG_VOID*)pui8Src, ui32Size); eError = TLStreamCommit(hStream, ui32Size); if ( PVRSRV_OK != eError ) { PVR_DPF_RETURN_RC(eError); } } PVR_DPF_RETURN_OK; }
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); } }
PVRSRV_ERROR TLServerCloseStreamKM(PTL_STREAM_DESC psSD) { PVRSRV_ERROR eError = PVRSRV_OK; PTL_GLOBAL_DATA psGD = TLGGD(); 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_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); } // Close and free the event handle resource used by this descriptor eError = OSEventObjectClose(psSD->hDataEvent); 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; } // Remove descriptor from stream object/list TLRemoveDescAndTryFreeStreamNode(psNode); // Free the stream descriptor object OSFREEMEM(psSD); // Assert the counter is sane after input data validated. PVR_ASSERT(TLGGD()->uiClientCnt > 0); TLGGD()->uiClientCnt--; PVR_DPF_RETURN_RC(eError); }
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); } // Does stream still exist? if (psNode->psStream == NULL) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_RESOURCE_UNAVAILABLE); } 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 TLStreamMarkEOS(IMG_HANDLE psStream) { PVRSRV_ERROR eError; IMG_UINT8* pData; PVR_DPF_ENTERED; if ( IMG_NULL == psStream ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS); } eError = DoTLStreamReserve(psStream, &pData, 0, 0, PVRSRVTL_PACKETTYPE_MARKER_EOS, NULL); if ( PVRSRV_OK != eError ) { PVR_DPF_RETURN_RC(eError); } PVR_DPF_RETURN_RC(TLStreamCommit(psStream, 0)); }
PVRSRV_ERROR TLStreamSync(IMG_HANDLE psStream) { PVRSRV_ERROR eError = PVRSRV_OK; PTL_STREAM psTmp; PVR_DPF_ENTERED; if ( IMG_NULL == psStream ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS); } psTmp = (PTL_STREAM)psStream; /* Signal clients only when data is available to read */ if (psTmp->ui32Read != psTmp->ui32Write) { eError = OSEventObjectSignal(psTmp->psNode->hDataEventObj); } PVR_DPF_RETURN_RC(eError); }
/* TLInit must only be called once at driver initialisation for one device. * An assert is provided to check this condition on debug builds. */ PVRSRV_ERROR TLInit(PVRSRV_DEVICE_NODE *psDevNode) { PVRSRV_ERROR eError; PVR_DPF_ENTERED; PVR_ASSERT(psDevNode); PVR_ASSERT(sTLGlobalData.psRgxDevNode==0); /* Store the RGX device node for later use in devmem buffer allocations */ sTLGlobalData.psRgxDevNode = (IMG_VOID*)psDevNode; /* Allocate a lock for TL global data, to be used while updating the TL data. * This is for making TL global data muti-thread safe */ eError = OSLockCreate (&sTLGlobalData.hTLGDLock, LOCK_TYPE_PASSIVE); if (eError != PVRSRV_OK) { goto e0; } /* Allocate the event object used to signal global TL events such as * new stream created */ eError = OSEventObjectCreate("TLGlobalEventObj", &sTLGlobalData.hTLEventObj); if (eError != PVRSRV_OK) { goto e1; } PVR_DPF_RETURN_OK; /* Don't allow the driver to start up on error */ e1: OSLockDestroy (sTLGlobalData.hTLGDLock); sTLGlobalData.hTLGDLock = NULL; e0: PVR_DPF_RETURN_RC (eError); }
PVRSRV_ERROR TLServerOpenStreamKM(IMG_PCHAR pszName, IMG_UINT32 ui32Mode, PTL_STREAM_DESC* ppsSD, DEVMEM_EXPORTCOOKIE** ppsBufCookie) { PVRSRV_ERROR eError = PVRSRV_OK; PVRSRV_ERROR eErrorEO = PVRSRV_OK; PTL_SNODE psNode = 0; TL_STREAM_DESC* psNewSD = 0; IMG_HANDLE hEvent; 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); 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_LOGR_IF_ERROR(eError, "OSEventObjectOpen"); do { if ((psNode = TLFindStreamNodeByName(pszName)) == NULL) { PVR_DPF((PVR_DBG_MESSAGE, "Stream %s does not exist, waiting...", pszName)); /* Will exit OK or with timeout, both cases safe to ignore */ eErrorEO = OSEventObjectWaitTimeout(hEvent, NO_STREAM_WAIT_PERIOD); } } while ((psNode == NULL) && (eErrorEO == PVRSRV_OK)); eError = OSEventObjectClose(hEvent); PVR_LOGR_IF_ERROR(eError, "OSEventObjectClose"); } /* 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) { PVR_DPF_RETURN_RC(eErrorEO); } PVR_DPF((PVR_DBG_ERROR, "Stream does not exist")); PVR_DPF_RETURN_RC(PVRSRV_ERROR_NOT_FOUND); } // Only one client/descriptor per stream supported if (psNode->psDesc != NULL) { PVR_DPF((PVR_DBG_ERROR, "Can not open stream, stream already opened")); PVR_DPF_RETURN_RC(PVRSRV_ERROR_ALREADY_OPEN); } // Create an event handle for this client to wait on when no data in stream // buffer. eError = OSEventObjectOpen(psNode->hDataEventObj, &hEvent); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "Not possible to open node's event object")); PVR_DPF_RETURN_RC(PVRSRV_ERROR_UNABLE_TO_CREATE_EVENT); } psNewSD = TLMakeStreamDesc(psNode, ui32Mode, hEvent); if (!psNewSD) { PVR_DPF((PVR_DBG_ERROR, "Not possible to make a new stream descriptor")); PVR_DPF_RETURN_RC(PVRSRV_ERROR_OUT_OF_MEMORY); } TLGGD()->uiClientCnt++; // Copy the export cookie back to the user mode API to enable access to // the stream buffer from user-mode process. *ppsBufCookie = TLStreamGetBufferCookie(psNode->psStream); psNode->psDesc = psNewSD; *ppsSD = psNewSD; PVR_DPF((PVR_DBG_VERBOSE, "TLServerOpenStreamKM evList=%p, evObj=%p", psNode->hDataEventObj, psNode->psDesc->hDataEvent)); PVR_DPF_RETURN_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); } // Does stream still exist? if (psNode->psStream == NULL) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_RESOURCE_UNAVAILABLE); } //PVR_DPF((PVR_DBG_VERBOSE, "TLServerAcquireDataKM evList=%p, evObj=%p", psSD->psNode->hDataEventObj, psSD->hDataEvent)); /* If stream still exists (has not be nulled in the mean time), * check for data in the associated stream buffer, sleep/wait if none */ while ((psNode->psStream != NULL) && ((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 ((psNode->psStream != NULL) && TLStreamEOS(psNode->psStream)) { eError = OSEventObjectWaitTimeout(psSD->hDataEvent, 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_RETURN_RC(eError); } } } /* Check we have been woken up because the stream has been destroyed? */ if (psNode->psStream == NULL) { PVR_DPF((PVR_DBG_VERBOSE, "TLAcquireDataKM awake, but stream now NULL")); PVR_DPF_RETURN_RC(PVRSRV_ERROR_RESOURCE_UNAVAILABLE); } /* 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 TLStreamCommit(IMG_HANDLE hStream, IMG_UINT32 ui32ReqSize) { PTL_STREAM psTmp; IMG_UINT32 ui32LRead, ui32OldWrite, ui32LWrite, ui32LPending; PVRSRV_ERROR eError; PVR_DPF_ENTERED; if ( IMG_NULL == hStream ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS); } psTmp = (PTL_STREAM)hStream; /* Get a local copy of the stream buffer parameters */ ui32LRead = psTmp->ui32Read ; ui32LWrite = psTmp->ui32Write ; ui32LPending = psTmp->ui32Pending ; ui32OldWrite = ui32LWrite; // Space in buffer is aligned ui32ReqSize = PVRSRVTL_ALIGN(ui32ReqSize); /* Sanity check. ReqSize + packet header size. */ if ( ui32LPending != ui32ReqSize + sizeof(PVRSRVTL_PACKETHDR) ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_MISUSE); } /* Update pointer to written data. */ ui32LWrite = (ui32LWrite + ui32LPending) % psTmp->ui32Size; /* and reset LPending to 0 since data are now submitted */ ui32LPending = NOTHING_PENDING; /* If we have transitioned from an empty buffer to a non-empty buffer, * signal any consumers that may be waiting. */ if (ui32OldWrite == ui32LRead && !psTmp->bNoSignalOnCommit) { /* Signal consumers that may be waiting */ eError = OSEventObjectSignal(psTmp->psNode->hDataEventObj); if ( eError != PVRSRV_OK) { PVR_DPF_RETURN_RC(eError); } } /* Calculate high water mark for debug purposes */ #if defined(TL_BUFFER_UTILIZATION) { IMG_UINT32 tmp = 0; if (ui32LWrite > ui32LRead) { tmp = (ui32LWrite-ui32LRead); } else if (ui32LWrite < ui32LRead) { tmp = (psTmp->ui32Size-ui32LRead+ui32LWrite); } /* else equal, ignore */ if (tmp > psTmp->ui32BufferUt) { psTmp->ui32BufferUt = tmp; } } #endif /* Update stream buffer parameters to match local copies */ psTmp->ui32Write = ui32LWrite ; psTmp->ui32Pending = ui32LPending ; PVR_DPF_RETURN_OK; }
static PVRSRV_ERROR DoTLStreamReserve(IMG_HANDLE hStream, IMG_UINT8 **ppui8Data, IMG_UINT32 ui32ReqSize, IMG_UINT32 ui32ReqSizeMin, PVRSRVTL_PACKETTYPE ePacketType, IMG_UINT32* pui32AvSpace) { PTL_STREAM psTmp; IMG_UINT32 *ui32Buf, ui32LRead, ui32LWrite, ui32LPending, lReqSizeAligned, lReqSizeActual; IMG_INT pad, iFreeSpace; PVR_DPF_ENTERED; if (pui32AvSpace) *pui32AvSpace = 0; if (( IMG_NULL == hStream )) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS); } psTmp = (PTL_STREAM)hStream; /* Assert used as the packet type parameter is currently only provided * by the TL APIs, not the calling client */ PVR_ASSERT((PVRSRVTL_PACKETTYPE_UNDEF < ePacketType) && (PVRSRVTL_PACKETTYPE_LAST >= ePacketType)); /* The buffer is only used in "rounded" (aligned) chunks */ lReqSizeAligned = PVRSRVTL_ALIGN(ui32ReqSize); /* Get a local copy of the stream buffer parameters */ ui32LRead = psTmp->ui32Read ; ui32LWrite = psTmp->ui32Write ; ui32LPending = psTmp->ui32Pending ; /* Multiple pending reserves are not supported. */ if ( NOTHING_PENDING != ui32LPending ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_NOT_READY); } if ( IMG_UINT16_MAX < lReqSizeAligned ) { psTmp->ui32Pending = NOTHING_PENDING; if (pui32AvSpace) { *pui32AvSpace = suggestAllocSize(ui32LRead, ui32LWrite, psTmp->ui32Size, ui32ReqSizeMin); } PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_FULL); } /* Prevent other threads from entering this region before we are done. * Not exactly a lock... */ psTmp->ui32Pending = 0; /* If there is enough contiguous space following the current Write * position then no padding is required */ if ( psTmp->ui32Size < ui32LWrite + lReqSizeAligned + sizeof(PVRSRVTL_PACKETHDR) ) { pad = psTmp->ui32Size - ui32LWrite; } else { pad = 0 ; } lReqSizeActual = lReqSizeAligned + sizeof(PVRSRVTL_PACKETHDR) + pad ; /* If this is a blocking reserve and there is not enough space then wait. */ if( psTmp->bBlock ) { if( psTmp->ui32Size < lReqSizeActual ) { psTmp->ui32Pending = NOTHING_PENDING; PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_MISUSE); } while ( ( cbSpaceLeft(ui32LRead, ui32LWrite, psTmp->ui32Size) <(IMG_INT) lReqSizeActual ) ) { OSEventObjectWait(psTmp->hProducerEvent); // update local copies. ui32LRead = psTmp->ui32Read ; ui32LWrite = psTmp->ui32Write ; } } /* The easy case: buffer has enough space to hold the requested packet (data + header) */ iFreeSpace = cbSpaceLeft(ui32LRead, ui32LWrite, psTmp->ui32Size); if ( iFreeSpace >=(IMG_INT) lReqSizeActual ) { if ( pad ) { /* Inserting padding packet. */ ui32Buf = (IMG_UINT32*)&psTmp->pbyBuffer[ui32LWrite]; *ui32Buf = PVRSRVTL_SET_PACKET_PADDING(pad-sizeof(PVRSRVTL_PACKETHDR)) ; /* CAUTION: the used pad value should always result in a properly * aligned ui32LWrite pointer, which in this case is 0 */ ui32LWrite = (ui32LWrite + pad) % psTmp->ui32Size; /* Detect unaligned pad value */ PVR_ASSERT( ui32LWrite == 0); } /* Insert size-stamped packet header */ ui32Buf = (IMG_UINT32*)&psTmp->pbyBuffer[ui32LWrite]; *ui32Buf = PVRSRVTL_SET_PACKET_HDR(ui32ReqSize, ePacketType); /* return the next position in the buffer to the user */ *ppui8Data = &psTmp->pbyBuffer[ ui32LWrite+sizeof(PVRSRVTL_PACKETHDR) ] ; /* update pending offset: size stamp + data */ ui32LPending = lReqSizeAligned + sizeof(PVRSRVTL_PACKETHDR) ; } /* The not so easy case: not enough space, decide how to handle data */ else { #if defined(DEBUG) /* Sanity check that the user is not trying to add more data than the * buffer size. Conditionally compile it out to ensure this check has * no impact to release performance */ if ( lReqSizeAligned+sizeof(PVRSRVTL_PACKETHDR) > psTmp->ui32Size ) { psTmp->ui32Pending = NOTHING_PENDING; PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_MISUSE); } #endif /* No data overwriting, insert write_failed flag and return */ if (psTmp->bDrop) { /* Caller should not try to use ppui8Data, * NULLify to give user a chance of avoiding memory corruption */ ppui8Data = IMG_NULL; /* This flag should not be inserted two consecutive times, so * check the last ui32 in case it was a packet drop packet. */ ui32Buf = ui32LWrite ? (IMG_UINT32*)&psTmp->pbyBuffer[ui32LWrite - sizeof(PVRSRVTL_PACKETHDR)] : // Previous four bytes are not guaranteed to be a packet header... (IMG_UINT32*)&psTmp->pbyBuffer[psTmp->ui32Size - PVRSRVTL_PACKET_ALIGNMENT]; if ( PVRSRVTL_PACKETTYPE_MOST_RECENT_WRITE_FAILED != GET_PACKET_TYPE( (PVRSRVTL_PACKETHDR*)ui32Buf ) ) { /* Insert size-stamped packet header */ ui32Buf = (IMG_UINT32*)&psTmp->pbyBuffer[ui32LWrite]; *ui32Buf = PVRSRVTL_SET_PACKET_WRITE_FAILED ; ui32LWrite += sizeof(PVRSRVTL_PACKETHDR); iFreeSpace -= sizeof(PVRSRVTL_PACKETHDR); } psTmp->ui32Write = ui32LWrite; psTmp->ui32Pending = NOTHING_PENDING; if (pui32AvSpace) { *pui32AvSpace = suggestAllocSize(ui32LRead, ui32LWrite, psTmp->ui32Size, ui32ReqSizeMin); } PVR_DPF_RETURN_RC(PVRSRV_ERROR_STREAM_FULL); } } /* Update stream. */ psTmp->ui32Write = ui32LWrite ; psTmp->ui32Pending = ui32LPending ; PVR_DPF_RETURN_OK; }
/******************************************************************************* * TL Server public API implementation. ******************************************************************************/ PVRSRV_ERROR TLStreamCreate(IMG_HANDLE *phStream, IMG_CHAR *szStreamName, IMG_UINT32 ui32Size, IMG_UINT32 ui32StreamFlags, TL_STREAM_SOURCECB pfProducerCB, IMG_PVOID pvProducerUD) { PTL_STREAM psTmp; PVRSRV_ERROR eError; IMG_HANDLE hEventList; PTL_SNODE psn = 0; IMG_CHAR pszBufferLabel[PRVSRVTL_MAX_STREAM_NAME_SIZE+20]; DEVMEM_FLAGS_T uiMemFlags = PVRSRV_MEMALLOCFLAG_CPU_READABLE | PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE | PVRSRV_MEMALLOCFLAG_GPU_READABLE | PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | PVRSRV_MEMALLOCFLAG_UNCACHED | /* GPU & CPU */ PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC | PVRSRV_MEMALLOCFLAG_CPU_WRITE_COMBINE; PVR_DPF_ENTERED; /* Sanity checks: */ /* non NULL handler required */ if ( NULL == phStream ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS); } if (OSStringLength(szStreamName) >= PRVSRVTL_MAX_STREAM_NAME_SIZE) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_INVALID_PARAMS); } /* Check if there already exists a stream with this name. */ psn = TLFindStreamNodeByName( szStreamName ); if ( IMG_NULL != psn ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_ALREADY_EXISTS); } /* Allocate stream structure container (stream struct) for the new stream */ psTmp = OSAllocZMem(sizeof(TL_STREAM)) ; if ( NULL == psTmp ) { PVR_DPF_RETURN_RC(PVRSRV_ERROR_OUT_OF_MEMORY); } OSStringCopy(psTmp->szName, szStreamName); if ( ui32StreamFlags & TL_FLAG_FORCE_FLUSH ) { psTmp->bWaitForEmptyOnDestroy = IMG_TRUE; } psTmp->bNoSignalOnCommit = (ui32StreamFlags&TL_FLAG_NO_SIGNAL_ON_COMMIT) ? IMG_TRUE : IMG_FALSE; if ( ui32StreamFlags & TL_FLAG_DROP_DATA ) { if ( ui32StreamFlags & TL_FLAG_BLOCKING_RESERVE ) { eError = PVRSRV_ERROR_INVALID_PARAMS; goto e0; } psTmp->bDrop = IMG_TRUE; } else if ( ui32StreamFlags & TL_FLAG_BLOCKING_RESERVE ) { /* Additional synchronization object required for this kind of stream */ psTmp->bBlock = IMG_TRUE; eError = OSEventObjectCreate(NULL, &psTmp->hProducerEventObj); if (eError != PVRSRV_OK) { goto e0; } /* Create an event handle for this kind of stream */ eError = OSEventObjectOpen(psTmp->hProducerEventObj, &psTmp->hProducerEvent); if (eError != PVRSRV_OK) { goto e1; } } /* Remember producer supplied CB and data for later */ psTmp->pfProducerCallback = (IMG_VOID(*)(IMG_VOID))pfProducerCB; psTmp->pvProducerUserData = pvProducerUD; /* Round the requested bytes to a multiple of array elements' size, eg round 3 to 4 */ psTmp->ui32Size = PVRSRVTL_ALIGN(ui32Size); psTmp->ui32Read = 0; psTmp->ui32Write = 0; psTmp->ui32Pending = NOTHING_PENDING; OSSNPrintf(pszBufferLabel, sizeof(pszBufferLabel), "TLStreamBuf-%s", szStreamName); /* Allocate memory for the circular buffer and export it to user space. */ eError = DevmemAllocateExportable( IMG_NULL, (IMG_HANDLE) TLGetGlobalRgxDevice(), (IMG_DEVMEM_SIZE_T)psTmp->ui32Size, (IMG_DEVMEM_ALIGN_T) OSGetPageSize(), uiMemFlags | PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE, pszBufferLabel, &psTmp->psStreamMemDesc); PVR_LOGG_IF_ERROR(eError, "DevmemAllocateExportable", e2); eError = DevmemAcquireCpuVirtAddr( psTmp->psStreamMemDesc, (IMG_VOID**) &psTmp->pbyBuffer ); PVR_LOGG_IF_ERROR(eError, "DevmemAcquireCpuVirtAddr", e3); eError = DevmemExport(psTmp->psStreamMemDesc, &(psTmp->sExportCookie)); PVR_LOGG_IF_ERROR(eError, "DevmemExport", e4); /* Synchronization object to synchronize with user side data transfers. */ eError = OSEventObjectCreate(psTmp->szName, &hEventList); if (eError != PVRSRV_OK) { goto e5; } /* Stream created, now reset the reference count to 1 */ psTmp->uiRefCount = 1; //Thread Safety: Not yet implemented eError = OSLockCreate(&psTmp->hLock, LOCK_TYPE_PASSIVE); //Thread Safety: Not yet implemented if (eError != PVRSRV_OK) //Thread Safety: Not yet implemented { //Thread Safety: Not yet implemented goto e6; //Thread Safety: Not yet implemented } /* Now remember the stream in the global TL structures */ psn = TLMakeSNode(hEventList, (TL_STREAM *)psTmp, 0); if (psn == NULL) { eError=PVRSRV_ERROR_OUT_OF_MEMORY; goto e7; } TLAddStreamNode(psn); /* Best effort signal, client wait timeout will ultimately let it find the * new stream if this fails, acceptable to avoid cleanup as it is tricky * at this point */ (void) OSEventObjectSignal(TLGGD()->hTLEventObj); /* Pass the newly created stream handle back to caller */ *phStream = (IMG_HANDLE)psTmp; PVR_DPF_RETURN_OK; e7: //Thread Safety: Not yet implemented OSLockDestroy(psTmp->hLock); //Thread Safety: Not yet implemented e6: OSEventObjectDestroy(hEventList); e5: DevmemUnexport(psTmp->psStreamMemDesc, &(psTmp->sExportCookie)); e4: DevmemReleaseCpuVirtAddr( psTmp->psStreamMemDesc ); e3: DevmemFree(psTmp->psStreamMemDesc); e2: OSEventObjectClose(psTmp->hProducerEvent); e1: OSEventObjectDestroy(psTmp->hProducerEventObj); e0: OSFREEMEM(psTmp); PVR_DPF_RETURN_RC(eError); }