/********************************************************************** * * Copyright (C) Imagination Technologies Ltd. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful but, except * as otherwise stated in writing, without any warranty; without even the * implied warranty of merchantability or fitness for a particular purpose. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * * Contact Information: * Imagination Technologies Ltd. * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK * ******************************************************************************/ #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) #ifndef AUTOCONF_INCLUDED #include #endif #endif #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "img_types.h" #include "services_headers.h" #include "mm.h" #include "pvrmmap.h" #include "mmap.h" #include "env_data.h" #include "proc.h" #include "mutex.h" #include "lock.h" #include "event.h" typedef struct PVRSRV_LINUX_EVENT_OBJECT_LIST_TAG { rwlock_t sLock; struct list_head sList; } PVRSRV_LINUX_EVENT_OBJECT_LIST; typedef struct PVRSRV_LINUX_EVENT_OBJECT_TAG { atomic_t sTimeStamp; IMG_UINT32 ui32TimeStampPrevious; #if defined(DEBUG) IMG_UINT ui32Stats; #endif wait_queue_head_t sWait; struct list_head sList; IMG_HANDLE hResItem; PVRSRV_LINUX_EVENT_OBJECT_LIST *psLinuxEventObjectList; } PVRSRV_LINUX_EVENT_OBJECT; PVRSRV_ERROR LinuxEventObjectListCreate(IMG_HANDLE *phEventObjectList) { PVRSRV_LINUX_EVENT_OBJECT_LIST *psEventObjectList; if(OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, sizeof(PVRSRV_LINUX_EVENT_OBJECT_LIST), (IMG_VOID **)&psEventObjectList, IMG_NULL, "Linux Event Object List") != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectCreate: failed to allocate memory for event list")); return PVRSRV_ERROR_OUT_OF_MEMORY; } INIT_LIST_HEAD(&psEventObjectList->sList); rwlock_init(&psEventObjectList->sLock); *phEventObjectList = (IMG_HANDLE *) psEventObjectList; return PVRSRV_OK; } PVRSRV_ERROR LinuxEventObjectListDestroy(IMG_HANDLE hEventObjectList) { PVRSRV_LINUX_EVENT_OBJECT_LIST *psEventObjectList = (PVRSRV_LINUX_EVENT_OBJECT_LIST *) hEventObjectList ; if(psEventObjectList) { IMG_BOOL bListEmpty; read_lock(&psEventObjectList->sLock); bListEmpty = list_empty(&psEventObjectList->sList); read_unlock(&psEventObjectList->sLock); if (!bListEmpty) { PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectListDestroy: Event List is not empty")); return PVRSRV_ERROR_UNABLE_TO_DESTROY_EVENT; } OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, sizeof(PVRSRV_LINUX_EVENT_OBJECT_LIST), psEventObjectList, IMG_NULL); } return PVRSRV_OK; } PVRSRV_ERROR LinuxEventObjectDelete(IMG_HANDLE hOSEventObjectList, IMG_HANDLE hOSEventObject) { if(hOSEventObjectList) { if(hOSEventObject) { PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject = (PVRSRV_LINUX_EVENT_OBJECT *)hOSEventObject; #if defined(DEBUG) PVR_DPF((PVR_DBG_MESSAGE, "LinuxEventObjectListDelete: Event object waits: %u", psLinuxEventObject->ui32Stats)); #endif if(ResManFreeResByPtr(psLinuxEventObject->hResItem, CLEANUP_WITH_POLL) != PVRSRV_OK) { return PVRSRV_ERROR_UNABLE_TO_DESTROY_EVENT; } return PVRSRV_OK; } } return PVRSRV_ERROR_UNABLE_TO_DESTROY_EVENT; } static PVRSRV_ERROR LinuxEventObjectDeleteCallback(IMG_PVOID pvParam, IMG_UINT32 ui32Param, IMG_BOOL bForceCleanup) { PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject = pvParam; PVRSRV_LINUX_EVENT_OBJECT_LIST *psLinuxEventObjectList = psLinuxEventObject->psLinuxEventObjectList; unsigned long ulLockFlags; PVR_UNREFERENCED_PARAMETER(ui32Param); PVR_UNREFERENCED_PARAMETER(bForceCleanup); write_lock_irqsave(&psLinuxEventObjectList->sLock, ulLockFlags); list_del(&psLinuxEventObject->sList); write_unlock_irqrestore(&psLinuxEventObjectList->sLock, ulLockFlags); #if defined(DEBUG) PVR_DPF((PVR_DBG_MESSAGE, "LinuxEventObjectDeleteCallback: Event object waits: %u", psLinuxEventObject->ui32Stats)); #endif OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, sizeof(PVRSRV_LINUX_EVENT_OBJECT), psLinuxEventObject, IMG_NULL); return PVRSRV_OK; } PVRSRV_ERROR LinuxEventObjectAdd(IMG_HANDLE hOSEventObjectList, IMG_HANDLE *phOSEventObject) { PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject; PVRSRV_LINUX_EVENT_OBJECT_LIST *psLinuxEventObjectList = (PVRSRV_LINUX_EVENT_OBJECT_LIST*)hOSEventObjectList; IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); PVRSRV_PER_PROCESS_DATA *psPerProc; unsigned long ulLockFlags; psPerProc = PVRSRVPerProcessData(ui32PID); if (psPerProc == IMG_NULL) { PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectAdd: Couldn't find per-process data")); return PVRSRV_ERROR_OUT_OF_MEMORY; } if(OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP, sizeof(PVRSRV_LINUX_EVENT_OBJECT), (IMG_VOID **)&psLinuxEventObject, IMG_NULL, "Linux Event Object") != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "LinuxEventObjectAdd: failed to allocate memory ")); return PVRSRV_ERROR_OUT_OF_MEMORY; } INIT_LIST_HEAD(&psLinuxEventObject->sList); atomic_set(&psLinuxEventObject->sTimeStamp, 0); psLinuxEventObject->ui32TimeStampPrevious = 0; #if defined(DEBUG) psLinuxEventObject->ui32Stats = 0; #endif init_waitqueue_head(&psLinuxEventObject->sWait); psLinuxEventObject->psLinuxEventObjectList = psLinuxEventObjectList; psLinuxEventObject->hResItem = ResManRegisterRes(psPerProc->hResManContext, RESMAN_TYPE_EVENT_OBJECT, psLinuxEventObject, 0, &LinuxEventObjectDeleteCallback); write_lock_irqsave(&psLinuxEventObjectList->sLock, ulLockFlags); list_add(&psLinuxEventObject->sList, &psLinuxEventObjectList->sList); write_unlock_irqrestore(&psLinuxEventObjectList->sLock, ulLockFlags); *phOSEventObject = psLinuxEventObject; return PVRSRV_OK; } PVRSRV_ERROR LinuxEventObjectSignal(IMG_HANDLE hOSEventObjectList) { PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject; PVRSRV_LINUX_EVENT_OBJECT_LIST *psLinuxEventObjectList = (PVRSRV_LINUX_EVENT_OBJECT_LIST*)hOSEventObjectList; struct list_head *psListEntry, *psList; psList = &psLinuxEventObjectList->sList; read_lock(&psLinuxEventObjectList->sLock); list_for_each(psListEntry, psList) { psLinuxEventObject = (PVRSRV_LINUX_EVENT_OBJECT *)list_entry(psListEntry, PVRSRV_LINUX_EVENT_OBJECT, sList); atomic_inc(&psLinuxEventObject->sTimeStamp); wake_up_interruptible(&psLinuxEventObject->sWait); } read_unlock(&psLinuxEventObjectList->sLock); return PVRSRV_OK; } PVRSRV_ERROR LinuxEventObjectWait(IMG_HANDLE hOSEventObject, IMG_UINT32 ui32MSTimeout) { IMG_UINT32 ui32TimeStamp; DEFINE_WAIT(sWait); PVRSRV_LINUX_EVENT_OBJECT *psLinuxEventObject = (PVRSRV_LINUX_EVENT_OBJECT *) hOSEventObject; IMG_UINT32 ui32TimeOutJiffies = msecs_to_jiffies(ui32MSTimeout); do { prepare_to_wait(&psLinuxEventObject->sWait, &sWait, TASK_INTERRUPTIBLE); ui32TimeStamp = (IMG_UINT32)atomic_read(&psLinuxEventObject->sTimeStamp); if(psLinuxEventObject->ui32TimeStampPrevious != ui32TimeStamp) { break; } LinuxUnLockMutex(&gPVRSRVLock); ui32TimeOutJiffies = (IMG_UINT32)schedule_timeout((IMG_INT32)ui32TimeOutJiffies); LinuxLockMutex(&gPVRSRVLock); #if defined(DEBUG) psLinuxEventObject->ui32Stats++; #endif } while (ui32TimeOutJiffies); finish_wait(&psLinuxEventObject->sWait, &sWait); psLinuxEventObject->ui32TimeStampPrevious = ui32TimeStamp; return ui32TimeOutJiffies ? PVRSRV_OK : PVRSRV_ERROR_TIMEOUT; }