blob: 7e160c37f3fa5732d55694313c2680f2a424a0f6 [file] [log] [blame]
/**********************************************************************
*
* 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. <gpl-support@imgtec.com>
* Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK
*
******************************************************************************/
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38))
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#endif
#include <asm/io.h>
#include <asm/page.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22))
#include <asm/system.h>
#endif
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <asm/hardirq.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#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;
}