Maemo patchset 20101501+0m5
[h-e-n] / drivers / gpu / pvr / osfunc.c
diff --git a/drivers/gpu/pvr/osfunc.c b/drivers/gpu/pvr/osfunc.c
new file mode 100644 (file)
index 0000000..848d6e8
--- /dev/null
@@ -0,0 +1,1627 @@
+/**********************************************************************
+ *
+ * Copyright(c) 2008 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
+ *
+ ******************************************************************************/
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/io.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/hugetlb.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 <linux/hardirq.h>
+#include <linux/timer.h>
+#include <linux/capability.h>
+#include <linux/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 "event.h"
+
+#define EVENT_OBJECT_TIMEOUT_MS                (100)
+
+#define HOST_ALLOC_MEM_USING_KMALLOC ((void *)0)
+#define HOST_ALLOC_MEM_USING_VMALLOC ((void *)1)
+
+#define LINUX_KMALLOC_LIMIT    PAGE_SIZE       /* 4k */
+
+#if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
+enum PVRSRV_ERROR OSAllocMem(u32 ui32Flags, u32 ui32Size,
+                       void **ppvCpuVAddr, void **phBlockAlloc)
+#else
+enum PVRSRV_ERROR _OSAllocMem(u32 ui32Flags, u32 ui32Size,
+                        void **ppvCpuVAddr, void **phBlockAlloc,
+                        char *pszFilename, u32 ui32Line)
+#endif
+{
+       u32 ui32Threshold;
+
+       PVR_UNREFERENCED_PARAMETER(ui32Flags);
+
+       /* determine whether to go straight to vmalloc */
+       ui32Threshold = LINUX_KMALLOC_LIMIT;
+
+       if (ui32Size > ui32Threshold) {
+#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
+               *ppvCpuVAddr = _VMallocWrapper(ui32Size, PVRSRV_HAP_CACHED,
+                                              pszFilename, ui32Line);
+#else
+               *ppvCpuVAddr = VMallocWrapper(ui32Size, PVRSRV_HAP_CACHED);
+#endif
+               if (!*ppvCpuVAddr)
+                       return PVRSRV_ERROR_OUT_OF_MEMORY;
+
+               if (phBlockAlloc)
+                       *phBlockAlloc = HOST_ALLOC_MEM_USING_VMALLOC;
+       } else {
+               /* default - try kmalloc first */
+
+#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
+               *ppvCpuVAddr = _KMallocWrapper(ui32Size, pszFilename, ui32Line);
+#else
+               *ppvCpuVAddr = KMallocWrapper(ui32Size);
+#endif
+
+               if (!*ppvCpuVAddr)
+                       return PVRSRV_ERROR_OUT_OF_MEMORY;
+
+               if (phBlockAlloc)
+                       *phBlockAlloc = HOST_ALLOC_MEM_USING_KMALLOC;
+
+       }
+
+       return PVRSRV_OK;
+}
+
+#if !defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
+void OSFreeMem(u32 ui32Flags, u32 ui32Size,
+                      void *pvCpuVAddr, void *hBlockAlloc)
+#else
+void _OSFreeMem(u32 ui32Flags, u32 ui32Size,
+                       void *pvCpuVAddr, void *hBlockAlloc,
+                       char *pszFilename, u32 ui32Line)
+#endif
+{
+       PVR_UNREFERENCED_PARAMETER(ui32Flags);
+
+       if (ui32Size > LINUX_KMALLOC_LIMIT) {
+#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
+               _VFreeWrapper(pvCpuVAddr, pszFilename, ui32Line);
+#else
+               VFreeWrapper(pvCpuVAddr);
+#endif
+       } else {
+#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
+               _KFreeWrapper(pvCpuVAddr, pszFilename, ui32Line);
+#else
+               KFreeWrapper(pvCpuVAddr);
+#endif
+       }
+}
+
+enum PVRSRV_ERROR OSAllocPages(u32 ui32AllocFlags,
+            u32 ui32Size,
+            void **ppvCpuVAddr, void **phOSMemHandle)
+{
+       struct LinuxMemArea *psLinuxMemArea;
+
+
+       switch (ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK) {
+       case PVRSRV_HAP_KERNEL_ONLY:
+               {
+                       psLinuxMemArea =
+                           NewVMallocLinuxMemArea(ui32Size, ui32AllocFlags);
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_OUT_OF_MEMORY;
+                       break;
+               }
+       case PVRSRV_HAP_SINGLE_PROCESS:
+               {
+
+                       psLinuxMemArea =
+                           NewAllocPagesLinuxMemArea(ui32Size, ui32AllocFlags);
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_OUT_OF_MEMORY;
+                       PVRMMapRegisterArea("Import Arena", psLinuxMemArea,
+                                           ui32AllocFlags);
+                       break;
+               }
+
+       case PVRSRV_HAP_MULTI_PROCESS:
+               {
+                       psLinuxMemArea =
+                           NewVMallocLinuxMemArea(ui32Size, ui32AllocFlags);
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_OUT_OF_MEMORY;
+                       PVRMMapRegisterArea("Import Arena", psLinuxMemArea,
+                                           ui32AllocFlags);
+                       break;
+               }
+       default:
+               PVR_DPF(PVR_DBG_ERROR, "OSAllocPages: invalid flags 0x%x\n",
+                        ui32AllocFlags);
+               *ppvCpuVAddr = NULL;
+               *phOSMemHandle = (void *) 0;
+               return PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
+       *ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea);
+       *phOSMemHandle = psLinuxMemArea;
+
+       LinuxMemAreaRegister(psLinuxMemArea);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSFreePages(u32 ui32AllocFlags, u32 ui32Bytes,
+           void *pvCpuVAddr, void *hOSMemHandle)
+{
+       struct LinuxMemArea *psLinuxMemArea;
+       PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
+
+       psLinuxMemArea = (struct LinuxMemArea *)hOSMemHandle;
+
+       switch (ui32AllocFlags & PVRSRV_HAP_MAPTYPE_MASK) {
+       case PVRSRV_HAP_KERNEL_ONLY:
+               break;
+       case PVRSRV_HAP_SINGLE_PROCESS:
+       case PVRSRV_HAP_MULTI_PROCESS:
+               if (PVRMMapRemoveRegisteredArea(psLinuxMemArea) != PVRSRV_OK) {
+                       PVR_DPF(PVR_DBG_ERROR,
+                                "OSFreePages(ui32AllocFlags=0x%08X, "
+                                "ui32Bytes=%ld, "
+                                "pvCpuVAddr=%p, hOSMemHandle=%p) FAILED!",
+                                ui32AllocFlags, ui32Bytes, pvCpuVAddr,
+                                hOSMemHandle);
+                       return PVRSRV_ERROR_GENERIC;
+               }
+               break;
+       default:
+               PVR_DPF(PVR_DBG_ERROR, "%s: invalid flags 0x%x\n",
+                        __func__, ui32AllocFlags);
+               return PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
+       LinuxMemAreaDeepFree(psLinuxMemArea);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSGetSubMemHandle(void *hOSMemHandle,
+                 u32 ui32ByteOffset,
+                 u32 ui32Bytes,
+                 u32 ui32Flags, void **phOSMemHandleRet)
+{
+       struct LinuxMemArea *psParentLinuxMemArea, *psLinuxMemArea;
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       psParentLinuxMemArea = (struct LinuxMemArea *)hOSMemHandle;
+
+       psLinuxMemArea =
+           NewSubLinuxMemArea(psParentLinuxMemArea, ui32ByteOffset, ui32Bytes);
+       if (!psLinuxMemArea) {
+               *phOSMemHandleRet = NULL;
+               return PVRSRV_ERROR_OUT_OF_MEMORY;
+       }
+       *phOSMemHandleRet = psLinuxMemArea;
+
+       if (ui32Flags & PVRSRV_HAP_KERNEL_ONLY)
+               return PVRSRV_OK;
+
+       if (psParentLinuxMemArea->eAreaType == LINUX_MEM_AREA_IO) {
+               eError = PVRMMapRegisterArea("Physical", psLinuxMemArea, 0);
+               if (eError != PVRSRV_OK)
+                       goto failed_register_area;
+       } else if (psParentLinuxMemArea->eAreaType ==
+                  LINUX_MEM_AREA_ALLOC_PAGES) {
+               eError = PVRMMapRegisterArea("Import Arena", psLinuxMemArea, 0);
+               if (eError != PVRSRV_OK)
+                       goto failed_register_area;
+       }
+
+       return PVRSRV_OK;
+
+failed_register_area:
+       *phOSMemHandleRet = NULL;
+       LinuxMemAreaDeepFree(psLinuxMemArea);
+       return eError;
+}
+
+enum PVRSRV_ERROR OSReleaseSubMemHandle(void *hOSMemHandle, u32 ui32Flags)
+{
+       struct LinuxMemArea *psParentLinuxMemArea, *psLinuxMemArea;
+       enum PVRSRV_ERROR eError;
+
+       psLinuxMemArea = (struct LinuxMemArea *)hOSMemHandle;
+       PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC);
+
+       psParentLinuxMemArea =
+           psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea;
+
+       if (!(ui32Flags & PVRSRV_HAP_KERNEL_ONLY)
+           && (psParentLinuxMemArea->eAreaType == LINUX_MEM_AREA_IO
+               || psParentLinuxMemArea->eAreaType ==
+               LINUX_MEM_AREA_ALLOC_PAGES)
+           ) {
+               eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea);
+               if (eError != PVRSRV_OK)
+                       return eError;
+       }
+       LinuxMemAreaDeepFree(psLinuxMemArea);
+
+       return PVRSRV_OK;
+}
+
+struct IMG_CPU_PHYADDR OSMemHandleToCpuPAddr(void *hOSMemHandle,
+                                            u32 ui32ByteOffset)
+{
+       PVR_ASSERT(hOSMemHandle);
+
+       return LinuxMemAreaToCpuPAddr(hOSMemHandle, ui32ByteOffset);
+}
+
+void OSMemCopy(void *pvDst, void *pvSrc, u32 ui32Size)
+{
+       memcpy(pvDst, pvSrc, ui32Size);
+}
+
+void OSMemSet(void *pvDest, u8 ui8Value, u32 ui32Size)
+{
+       memset(pvDest, (int)ui8Value, (size_t) ui32Size);
+}
+
+char *OSStringCopy(char *pszDest, const char *pszSrc)
+{
+       return strcpy(pszDest, pszSrc);
+}
+
+s32 OSSNPrintf(char *pStr, u32 ui32Size, const char *pszFormat, ...)
+{
+       va_list argList;
+       s32 iCount;
+
+       va_start(argList, pszFormat);
+       iCount = vsnprintf(pStr, (size_t) ui32Size, pszFormat, argList);
+       va_end(argList);
+
+       return iCount;
+}
+
+void OSBreakResourceLock(struct PVRSRV_RESOURCE *psResource, u32 ui32ID)
+{
+       volatile u32 *pui32Access = (volatile u32 *)&psResource->ui32Lock;
+
+       if (*pui32Access)
+               if (psResource->ui32ID == ui32ID) {
+                       psResource->ui32ID = 0;
+                       *pui32Access = 0;
+               } else {
+                       PVR_DPF(PVR_DBG_MESSAGE, "OSBreakResourceLock: "
+                               "Resource is not locked for this process.");
+               }
+       else
+               PVR_DPF(PVR_DBG_MESSAGE,
+                        "OSBreakResourceLock: Resource is not locked");
+}
+
+enum PVRSRV_ERROR OSCreateResource(struct PVRSRV_RESOURCE *psResource)
+{
+       psResource->ui32ID = 0;
+       psResource->ui32Lock = 0;
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSDestroyResource(struct PVRSRV_RESOURCE *psResource)
+{
+       OSBreakResourceLock(psResource, psResource->ui32ID);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSInitEnvData(void **ppvEnvSpecificData)
+{
+       struct ENV_DATA *psEnvData;
+
+       if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct ENV_DATA),
+            (void *)&psEnvData, NULL) != PVRSRV_OK)
+               return PVRSRV_ERROR_GENERIC;
+
+       memset(psEnvData, 0, sizeof(*psEnvData));
+
+       if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
+            PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE,
+            &psEnvData->pvBridgeData, NULL) != PVRSRV_OK) {
+               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct ENV_DATA),
+                         psEnvData, NULL);
+               return PVRSRV_ERROR_GENERIC;
+       }
+
+       psEnvData->bMISRInstalled = IMG_FALSE;
+       psEnvData->bLISRInstalled = IMG_FALSE;
+
+       *ppvEnvSpecificData = psEnvData;
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSDeInitEnvData(void *pvEnvSpecificData)
+{
+       struct ENV_DATA *psEnvData = (struct ENV_DATA *)pvEnvSpecificData;
+
+       PVR_ASSERT(!psEnvData->bMISRInstalled);
+       PVR_ASSERT(!psEnvData->bLISRInstalled);
+
+       OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP,
+                 PVRSRV_MAX_BRIDGE_IN_SIZE + PVRSRV_MAX_BRIDGE_OUT_SIZE,
+                 psEnvData->pvBridgeData, NULL);
+
+       OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct ENV_DATA),
+                 pvEnvSpecificData, NULL);
+
+       return PVRSRV_OK;
+}
+
+void OSReleaseThreadQuanta(void)
+{
+       schedule();
+}
+
+u32 OSClockus(void)
+{
+       unsigned long time, j = jiffies;
+
+       time = j * (1000000 / HZ);
+
+       return time;
+}
+
+void OSWaitus(u32 ui32Timeus)
+{
+       udelay(ui32Timeus);
+}
+
+u32 OSGetCurrentProcessIDKM(void)
+{
+       if (in_interrupt())
+               return KERNEL_ID;
+       return task_tgid_nr(current);
+}
+
+u32 OSGetPageSize(void)
+{
+       return PAGE_SIZE;
+}
+
+static irqreturn_t DeviceISRWrapper(int irq, void *dev_id)
+{
+       struct PVRSRV_DEVICE_NODE *psDeviceNode;
+       IMG_BOOL bStatus = IMG_FALSE;
+
+       psDeviceNode = (struct PVRSRV_DEVICE_NODE *)dev_id;
+       if (!psDeviceNode) {
+               PVR_DPF(PVR_DBG_ERROR, "DeviceISRWrapper: invalid params\n");
+               goto out;
+       }
+
+       bStatus = PVRSRVDeviceLISR(psDeviceNode);
+
+       if (bStatus) {
+               struct SYS_DATA *psSysData = psDeviceNode->psSysData;
+               struct ENV_DATA *psEnvData =
+                               (struct ENV_DATA *)psSysData->pvEnvSpecificData;
+
+               queue_work(psEnvData->psMISRWorkqueue, &psEnvData->sMISRWork);
+       }
+
+out:
+       return bStatus ? IRQ_HANDLED : IRQ_NONE;
+}
+
+enum PVRSRV_ERROR OSInstallDeviceLISR(void *pvSysData,
+                                u32 ui32Irq,
+                                char *pszISRName, void *pvDeviceNode)
+{
+       struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
+       struct ENV_DATA *psEnvData =
+                       (struct ENV_DATA *)psSysData->pvEnvSpecificData;
+
+       if (psEnvData->bLISRInstalled) {
+               PVR_DPF(PVR_DBG_ERROR, "OSInstallDeviceLISR: "
+                       "An ISR has already been installed: IRQ %d cookie %x",
+                       psEnvData->ui32IRQ, psEnvData->pvISRCookie);
+               return PVRSRV_ERROR_GENERIC;
+       }
+
+       PVR_TRACE("Installing device LISR %s on IRQ %d with cookie %x",
+                  pszISRName, ui32Irq, pvDeviceNode);
+
+       if (request_irq(ui32Irq, DeviceISRWrapper, IRQF_SHARED, pszISRName,
+                       pvDeviceNode)) {
+               PVR_DPF(PVR_DBG_ERROR, "OSInstallDeviceLISR: "
+                                   "Couldn't install device LISR on IRQ %d",
+                        ui32Irq);
+
+               return PVRSRV_ERROR_GENERIC;
+       }
+
+       psEnvData->ui32IRQ = ui32Irq;
+       psEnvData->pvISRCookie = pvDeviceNode;
+       psEnvData->bLISRInstalled = IMG_TRUE;
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSUninstallDeviceLISR(void *pvSysData)
+{
+       struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
+       struct ENV_DATA *psEnvData =
+                               (struct ENV_DATA *)psSysData->pvEnvSpecificData;
+
+       if (!psEnvData->bLISRInstalled) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "OSUninstallDeviceLISR: No LISR has been installed");
+               return PVRSRV_ERROR_GENERIC;
+       }
+
+       PVR_TRACE("Uninstalling device LISR on IRQ %d with cookie %x",
+                  psEnvData->ui32IRQ, psEnvData->pvISRCookie);
+
+       free_irq(psEnvData->ui32IRQ, psEnvData->pvISRCookie);
+
+       psEnvData->bLISRInstalled = IMG_FALSE;
+
+       return PVRSRV_OK;
+}
+
+static void MISRWrapper(struct work_struct *work)
+{
+       struct ENV_DATA *psEnvData = container_of(work, struct ENV_DATA,
+                                                 sMISRWork);
+       struct SYS_DATA *psSysData = (struct SYS_DATA *)psEnvData->pvSysData;
+       PVRSRVMISR(psSysData);
+}
+
+enum PVRSRV_ERROR OSInstallMISR(void *pvSysData)
+{
+       struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
+       struct ENV_DATA *psEnvData =
+                               (struct ENV_DATA *)psSysData->pvEnvSpecificData;
+
+       if (psEnvData->bMISRInstalled) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "OSInstallMISR: An MISR has already been installed");
+               return PVRSRV_ERROR_GENERIC;
+       }
+
+       PVR_TRACE("Installing MISR with cookie %x", pvSysData);
+
+       psEnvData->pvSysData = pvSysData;
+       psEnvData->psMISRWorkqueue = create_singlethread_workqueue("sgx_misr");
+       INIT_WORK(&psEnvData->sMISRWork, MISRWrapper);
+
+       psEnvData->bMISRInstalled = IMG_TRUE;
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSUninstallMISR(void *pvSysData)
+{
+       struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
+       struct ENV_DATA *psEnvData =
+                               (struct ENV_DATA *)psSysData->pvEnvSpecificData;
+
+       if (!psEnvData->bMISRInstalled) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "OSUninstallMISR: No MISR has been installed");
+               return PVRSRV_ERROR_GENERIC;
+       }
+
+       PVR_TRACE("Uninstalling MISR");
+
+       flush_workqueue(psEnvData->psMISRWorkqueue);
+       destroy_workqueue(psEnvData->psMISRWorkqueue);
+
+       psEnvData->bMISRInstalled = IMG_FALSE;
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSScheduleMISR(void *pvSysData)
+{
+       struct SYS_DATA *psSysData = (struct SYS_DATA *)pvSysData;
+       struct ENV_DATA *psEnvData =
+                               (struct ENV_DATA *)psSysData->pvEnvSpecificData;
+
+       if (psEnvData->bMISRInstalled)
+               queue_work(psEnvData->psMISRWorkqueue, &psEnvData->sMISRWork);
+
+       return PVRSRV_OK;
+}
+
+
+#define        OS_TAS(p)       xchg((p), 1)
+enum PVRSRV_ERROR OSLockResource(struct PVRSRV_RESOURCE *psResource, u32 ui32ID)
+{
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       if (!OS_TAS(&psResource->ui32Lock))
+               psResource->ui32ID = ui32ID;
+       else
+               eError = PVRSRV_ERROR_GENERIC;
+
+       return eError;
+}
+
+enum PVRSRV_ERROR OSUnlockResource(struct PVRSRV_RESOURCE *psResource,
+                                  u32 ui32ID)
+{
+       volatile u32 *pui32Access = (volatile u32 *)&psResource->ui32Lock;
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       if (*pui32Access) {
+               if (psResource->ui32ID == ui32ID) {
+                       psResource->ui32ID = 0;
+                       *pui32Access = 0;
+               } else {
+                       PVR_DPF(PVR_DBG_ERROR, "OSUnlockResource: "
+                              "Resource %p is not locked with expected value.",
+                               psResource);
+                       PVR_DPF(PVR_DBG_MESSAGE, "Should be %x is actually %x",
+                                ui32ID, psResource->ui32ID);
+                       eError = PVRSRV_ERROR_GENERIC;
+               }
+       } else {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "OSUnlockResource: Resource %p is not locked",
+                        psResource);
+               eError = PVRSRV_ERROR_GENERIC;
+       }
+
+       return eError;
+}
+
+struct IMG_CPU_PHYADDR OSMapLinToCPUPhys(void *pvLinAddr)
+{
+       struct IMG_CPU_PHYADDR CpuPAddr;
+
+       CpuPAddr.uiAddr = (u32) VMallocToPhys(pvLinAddr);
+
+       return CpuPAddr;
+}
+
+void __iomem *OSMapPhysToLin(struct IMG_CPU_PHYADDR BasePAddr, u32 ui32Bytes,
+                        u32 ui32MappingFlags, void **phOSMemHandle)
+{
+       if (phOSMemHandle)
+               *phOSMemHandle = (void *) 0;
+
+       if (ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY) {
+               void __iomem *pvIORemapCookie;
+               pvIORemapCookie =
+                   IORemapWrapper(BasePAddr, ui32Bytes, ui32MappingFlags);
+               if (pvIORemapCookie == NULL)
+                       return NULL;
+               return pvIORemapCookie;
+       } else {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "OSMapPhysToLin should only be used with "
+                        "PVRSRV_HAP_KERNEL_ONLY  "
+                        "(Use OSReservePhys otherwise)");
+               *phOSMemHandle = (void *) 0;
+               return NULL;
+       }
+
+       PVR_ASSERT(0);
+       return NULL;
+}
+
+IMG_BOOL
+OSUnMapPhysToLin(void __iomem *pvLinAddr, u32 ui32Bytes,
+                u32 ui32MappingFlags, void *hPageAlloc)
+{
+       PVR_TRACE("%s: unmapping %d bytes from 0x%08x", __func__,
+                  ui32Bytes, pvLinAddr);
+
+       PVR_UNREFERENCED_PARAMETER(hPageAlloc);
+
+       if (ui32MappingFlags & PVRSRV_HAP_KERNEL_ONLY) {
+               IOUnmapWrapper(pvLinAddr);
+               return IMG_TRUE;
+       } else {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "OSUnMapPhysToLin should only be used with "
+                        "PVRSRV_HAP_KERNEL_ONLY "
+                        " (Use OSUnReservePhys otherwise)");
+               return IMG_FALSE;
+       }
+
+       PVR_ASSERT(0);
+       return IMG_FALSE;
+}
+
+static enum PVRSRV_ERROR RegisterExternalMem(struct IMG_SYS_PHYADDR *pBasePAddr,
+                   void *pvCPUVAddr, u32 ui32Bytes,
+                   IMG_BOOL bPhysContig, u32 ui32MappingFlags,
+                   void **phOSMemHandle)
+{
+       struct LinuxMemArea *psLinuxMemArea;
+
+       switch (ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) {
+       case PVRSRV_HAP_KERNEL_ONLY:
+               {
+                       psLinuxMemArea =
+                           NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr,
+                                                     ui32Bytes, bPhysContig,
+                                                     ui32MappingFlags);
+
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_GENERIC;
+                       break;
+               }
+       case PVRSRV_HAP_SINGLE_PROCESS:
+               {
+                       psLinuxMemArea =
+                           NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr,
+                                                     ui32Bytes, bPhysContig,
+                                                     ui32MappingFlags);
+
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_GENERIC;
+                       PVRMMapRegisterArea("Physical", psLinuxMemArea,
+                                           ui32MappingFlags);
+                       break;
+               }
+       case PVRSRV_HAP_MULTI_PROCESS:
+               {
+                       psLinuxMemArea =
+                           NewExternalKVLinuxMemArea(pBasePAddr, pvCPUVAddr,
+                                                     ui32Bytes, bPhysContig,
+                                                     ui32MappingFlags);
+
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_GENERIC;
+                       PVRMMapRegisterArea("Physical", psLinuxMemArea,
+                                           ui32MappingFlags);
+                       break;
+               }
+       default:
+               PVR_DPF(PVR_DBG_ERROR, "OSRegisterMem : invalid flags 0x%x\n",
+                        ui32MappingFlags);
+               *phOSMemHandle = (void *) 0;
+               return PVRSRV_ERROR_GENERIC;
+       }
+
+       *phOSMemHandle = (void *) psLinuxMemArea;
+
+       LinuxMemAreaRegister(psLinuxMemArea);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSRegisterMem(struct IMG_CPU_PHYADDR BasePAddr,
+             void *pvCPUVAddr,
+             u32 ui32Bytes,
+             u32 ui32MappingFlags, void **phOSMemHandle)
+{
+       struct IMG_SYS_PHYADDR SysPAddr = SysCpuPAddrToSysPAddr(BasePAddr);
+
+       return RegisterExternalMem(&SysPAddr, pvCPUVAddr, ui32Bytes, IMG_TRUE,
+                                  ui32MappingFlags, phOSMemHandle);
+}
+
+enum PVRSRV_ERROR OSRegisterDiscontigMem(struct IMG_SYS_PHYADDR *pBasePAddr,
+                                   void *pvCPUVAddr, u32 ui32Bytes,
+                                   u32 ui32MappingFlags,
+                                   void **phOSMemHandle)
+{
+       return RegisterExternalMem(pBasePAddr, pvCPUVAddr, ui32Bytes,
+                                  IMG_FALSE, ui32MappingFlags, phOSMemHandle);
+}
+
+enum PVRSRV_ERROR OSUnRegisterMem(void *pvCpuVAddr,
+               u32 ui32Bytes,
+               u32 ui32MappingFlags, void *hOSMemHandle)
+{
+       struct LinuxMemArea *psLinuxMemArea = (struct LinuxMemArea *)
+                                                               hOSMemHandle;
+
+       PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
+
+       switch (ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) {
+       case PVRSRV_HAP_KERNEL_ONLY:
+               break;
+       case PVRSRV_HAP_SINGLE_PROCESS:
+       case PVRSRV_HAP_MULTI_PROCESS:
+               {
+                       if (PVRMMapRemoveRegisteredArea(psLinuxMemArea) !=
+                           PVRSRV_OK) {
+                               PVR_DPF(PVR_DBG_ERROR,
+                                        "%s(%p, %d, 0x%08X, %p) FAILED!",
+                                        __func__, pvCpuVAddr, ui32Bytes,
+                                        ui32MappingFlags, hOSMemHandle);
+                               return PVRSRV_ERROR_GENERIC;
+                       }
+                       break;
+               }
+       default:
+               {
+                       PVR_DPF(PVR_DBG_ERROR,
+                                "OSUnRegisterMem : invalid flags 0x%x",
+                                ui32MappingFlags);
+                       return PVRSRV_ERROR_INVALID_PARAMS;
+               }
+       }
+
+       LinuxMemAreaDeepFree(psLinuxMemArea);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSUnRegisterDiscontigMem(void *pvCpuVAddr, u32 ui32Bytes,
+                                     u32 ui32Flags, void *hOSMemHandle)
+{
+       return OSUnRegisterMem(pvCpuVAddr, ui32Bytes, ui32Flags, hOSMemHandle);
+}
+
+enum PVRSRV_ERROR OSReservePhys(struct IMG_CPU_PHYADDR BasePAddr,
+             u32 ui32Bytes, u32 ui32MappingFlags, void **ppvCpuVAddr,
+             void **phOSMemHandle)
+{
+       struct LinuxMemArea *psLinuxMemArea;
+
+       switch (ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) {
+       case PVRSRV_HAP_KERNEL_ONLY:
+               {
+
+                       psLinuxMemArea =
+                           NewIORemapLinuxMemArea(BasePAddr, ui32Bytes,
+                                                  ui32MappingFlags);
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_GENERIC;
+                       break;
+               }
+       case PVRSRV_HAP_SINGLE_PROCESS:
+               {
+                       psLinuxMemArea =
+                           NewIOLinuxMemArea(BasePAddr, ui32Bytes,
+                                             ui32MappingFlags);
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_GENERIC;
+                       PVRMMapRegisterArea("Physical", psLinuxMemArea,
+                                           ui32MappingFlags);
+                       break;
+               }
+       case PVRSRV_HAP_MULTI_PROCESS:
+               {
+                       psLinuxMemArea =
+                           NewIORemapLinuxMemArea(BasePAddr, ui32Bytes,
+                                                  ui32MappingFlags);
+                       if (!psLinuxMemArea)
+                               return PVRSRV_ERROR_GENERIC;
+                       PVRMMapRegisterArea("Physical", psLinuxMemArea,
+                                           ui32MappingFlags);
+                       break;
+               }
+       default:
+               PVR_DPF(PVR_DBG_ERROR, "OSMapPhysToLin : invalid flags 0x%x\n",
+                        ui32MappingFlags);
+               *ppvCpuVAddr = NULL;
+               *phOSMemHandle = (void *) 0;
+               return PVRSRV_ERROR_GENERIC;
+       }
+
+       *phOSMemHandle = (void *) psLinuxMemArea;
+       *ppvCpuVAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea);
+
+       LinuxMemAreaRegister(psLinuxMemArea);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSUnReservePhys(void *pvCpuVAddr,
+               u32 ui32Bytes, u32 ui32MappingFlags, void *hOSMemHandle)
+{
+       struct LinuxMemArea *psLinuxMemArea;
+       PVR_UNREFERENCED_PARAMETER(pvCpuVAddr);
+
+       psLinuxMemArea = (struct LinuxMemArea *)hOSMemHandle;
+
+       switch (ui32MappingFlags & PVRSRV_HAP_MAPTYPE_MASK) {
+       case PVRSRV_HAP_KERNEL_ONLY:
+               break;
+       case PVRSRV_HAP_SINGLE_PROCESS:
+       case PVRSRV_HAP_MULTI_PROCESS:
+               {
+                       if (PVRMMapRemoveRegisteredArea(psLinuxMemArea) !=
+                           PVRSRV_OK) {
+                               PVR_DPF(PVR_DBG_ERROR,
+                                        "%s(%p, %d, 0x%08X, %p) FAILED!",
+                                        __func__, pvCpuVAddr, ui32Bytes,
+                                        ui32MappingFlags, hOSMemHandle);
+                               return PVRSRV_ERROR_GENERIC;
+                       }
+                       break;
+               }
+       default:
+               {
+                       PVR_DPF(PVR_DBG_ERROR,
+                                "OSUnMapPhysToLin : invalid flags 0x%x",
+                                ui32MappingFlags);
+                       return PVRSRV_ERROR_INVALID_PARAMS;
+               }
+       }
+
+       LinuxMemAreaDeepFree(psLinuxMemArea);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSBaseAllocContigMemory(u32 ui32Size, void **pvLinAddr,
+                                    struct IMG_CPU_PHYADDR *psPhysAddr)
+{
+       PVR_UNREFERENCED_PARAMETER(ui32Size);
+       PVR_UNREFERENCED_PARAMETER(pvLinAddr);
+       PVR_UNREFERENCED_PARAMETER(psPhysAddr);
+       PVR_DPF(PVR_DBG_ERROR, "%s: Not available", __func__);
+
+       return PVRSRV_ERROR_OUT_OF_MEMORY;
+}
+
+enum PVRSRV_ERROR OSBaseFreeContigMemory(u32 ui32Size, void *pvLinAddr,
+                                   struct IMG_CPU_PHYADDR psPhysAddr)
+{
+       PVR_UNREFERENCED_PARAMETER(ui32Size);
+       PVR_UNREFERENCED_PARAMETER(pvLinAddr);
+       PVR_UNREFERENCED_PARAMETER(psPhysAddr);
+
+       PVR_DPF(PVR_DBG_WARNING, "%s: Not available", __func__);
+       return PVRSRV_OK;
+}
+
+u32 OSReadHWReg(void __iomem *pvLinRegBaseAddr, u32 ui32Offset)
+{
+       return (u32)readl(pvLinRegBaseAddr + ui32Offset);
+}
+
+void OSWriteHWReg(void __iomem *pvLinRegBaseAddr, u32 ui32Offset, u32 ui32Value)
+{
+       writel(ui32Value, pvLinRegBaseAddr + ui32Offset);
+}
+
+struct TIMER_CALLBACK_DATA {
+       void (*pfnTimerFunc)(void *);
+       void *pvData;
+       struct timer_list sTimer;
+       u32 ui32Delay;
+       IMG_BOOL bActive;
+};
+
+static void OSTimerCallbackWrapper(unsigned long ui32Data)
+{
+       struct TIMER_CALLBACK_DATA *psTimerCBData =
+                                       (struct TIMER_CALLBACK_DATA *)ui32Data;
+
+       if (!psTimerCBData->bActive)
+               return;
+
+       psTimerCBData->pfnTimerFunc(psTimerCBData->pvData);
+
+       mod_timer(&psTimerCBData->sTimer, psTimerCBData->ui32Delay + jiffies);
+}
+
+void *OSAddTimer(void (*pfnTimerFunc)(void *), void *pvData, u32 ui32MsTimeout)
+{
+       struct TIMER_CALLBACK_DATA *psTimerCBData;
+
+       if (!pfnTimerFunc) {
+               PVR_DPF(PVR_DBG_ERROR, "OSAddTimer: passed invalid callback");
+               return NULL;
+       }
+
+       if (OSAllocMem(PVRSRV_OS_NON_PAGEABLE_HEAP,
+                      sizeof(struct TIMER_CALLBACK_DATA),
+                      (void **) &psTimerCBData, NULL) != PVRSRV_OK) {
+               PVR_DPF(PVR_DBG_ERROR,
+                        "OSAddTimer: failed to allocate memory");
+               return NULL;
+       }
+
+       psTimerCBData->pfnTimerFunc = pfnTimerFunc;
+       psTimerCBData->pvData = pvData;
+       psTimerCBData->bActive = IMG_FALSE;
+
+       psTimerCBData->ui32Delay = ((HZ * ui32MsTimeout) < 1000)
+           ? 1 : ((HZ * ui32MsTimeout) / 1000);
+
+       init_timer(&psTimerCBData->sTimer);
+
+       psTimerCBData->sTimer.function = OSTimerCallbackWrapper;
+       psTimerCBData->sTimer.data = (u32) psTimerCBData;
+       psTimerCBData->sTimer.expires = psTimerCBData->ui32Delay + jiffies;
+
+       return (void *)psTimerCBData;
+}
+
+enum PVRSRV_ERROR OSRemoveTimer(void *hTimer)
+{
+       struct TIMER_CALLBACK_DATA *psTimerCBData =
+                                       (struct TIMER_CALLBACK_DATA *)hTimer;
+
+       OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP,
+                 sizeof(struct TIMER_CALLBACK_DATA),
+                 psTimerCBData, NULL);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSEnableTimer(void *hTimer)
+{
+       struct TIMER_CALLBACK_DATA *psTimerCBData =
+                                       (struct TIMER_CALLBACK_DATA *)hTimer;
+
+       psTimerCBData->bActive = IMG_TRUE;
+
+       psTimerCBData->sTimer.expires = psTimerCBData->ui32Delay + jiffies;
+       add_timer(&psTimerCBData->sTimer);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSDisableTimer(void *hTimer)
+{
+       struct TIMER_CALLBACK_DATA *psTimerCBData =
+                                       (struct TIMER_CALLBACK_DATA *)hTimer;
+
+       psTimerCBData->bActive = IMG_FALSE;
+
+       del_timer_sync(&psTimerCBData->sTimer);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSEventObjectCreate(const char *pszName,
+                                struct PVRSRV_EVENTOBJECT *psEventObject)
+{
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       if (psEventObject) {
+               if (pszName) {
+
+                       strncpy(psEventObject->szName, pszName,
+                               EVENTOBJNAME_MAXLENGTH);
+               } else {
+
+                       static u16 ui16NameIndex;
+                       snprintf(psEventObject->szName, EVENTOBJNAME_MAXLENGTH,
+                                "PVRSRV_EVENTOBJECT_%d", ui16NameIndex++);
+               }
+
+               if (LinuxEventObjectListCreate(&psEventObject->hOSEventKM) !=
+                   PVRSRV_OK)
+                       eError = PVRSRV_ERROR_OUT_OF_MEMORY;
+
+       } else {
+               PVR_DPF(PVR_DBG_ERROR, "OSEventObjectCreate: "
+                       "psEventObject is not a valid pointer");
+               eError = PVRSRV_ERROR_GENERIC;
+       }
+
+       return eError;
+
+}
+
+enum PVRSRV_ERROR OSEventObjectDestroy(struct PVRSRV_EVENTOBJECT *psEventObject)
+{
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       if (psEventObject) {
+               if (psEventObject->hOSEventKM) {
+                       LinuxEventObjectListDestroy(psEventObject->hOSEventKM);
+               } else {
+                       PVR_DPF(PVR_DBG_ERROR, "OSEventObjectDestroy: "
+                               "hOSEventKM is not a valid pointer");
+                       eError = PVRSRV_ERROR_INVALID_PARAMS;
+               }
+       } else {
+               PVR_DPF(PVR_DBG_ERROR, "OSEventObjectDestroy: "
+                               "psEventObject is not a valid pointer");
+               eError = PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
+       return eError;
+}
+
+enum PVRSRV_ERROR OSEventObjectWait(void *hOSEventKM)
+{
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       if (hOSEventKM) {
+               eError =
+                   LinuxEventObjectWait(hOSEventKM, EVENT_OBJECT_TIMEOUT_MS);
+       } else {
+               PVR_DPF(PVR_DBG_ERROR, "OSEventObjectWait: "
+                       "hOSEventKM is not a valid handle");
+               eError = PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
+       return eError;
+}
+
+enum PVRSRV_ERROR OSEventObjectOpen(struct PVRSRV_EVENTOBJECT *psEventObject,
+                              void **phOSEvent)
+{
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       if (psEventObject) {
+               if (LinuxEventObjectAdd(psEventObject->hOSEventKM, phOSEvent) !=
+                   PVRSRV_OK) {
+                       PVR_DPF(PVR_DBG_ERROR, "LinuxEventObjectAdd: failed");
+                       eError = PVRSRV_ERROR_INVALID_PARAMS;
+               }
+
+       } else {
+               PVR_DPF(PVR_DBG_ERROR, "OSEventObjectCreate: "
+                       "psEventObject is not a valid pointer");
+               eError = PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
+       return eError;
+}
+
+enum PVRSRV_ERROR OSEventObjectClose(struct PVRSRV_EVENTOBJECT *psEventObject,
+                               void *hOSEventKM)
+{
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       if (psEventObject) {
+               if (LinuxEventObjectDelete
+                   (psEventObject->hOSEventKM, hOSEventKM) != PVRSRV_OK) {
+                       PVR_DPF(PVR_DBG_ERROR,
+                                "LinuxEventObjectDelete: failed");
+                       eError = PVRSRV_ERROR_INVALID_PARAMS;
+               }
+
+       } else {
+               PVR_DPF(PVR_DBG_ERROR, "OSEventObjectDestroy: "
+                               "psEventObject is not a valid pointer");
+               eError = PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
+       return eError;
+
+}
+
+enum PVRSRV_ERROR OSEventObjectSignal(void *hOSEventKM)
+{
+       enum PVRSRV_ERROR eError = PVRSRV_OK;
+
+       if (hOSEventKM) {
+               eError = LinuxEventObjectSignal(hOSEventKM);
+       } else {
+               PVR_DPF(PVR_DBG_ERROR, "OSEventObjectSignal: "
+                       "hOSEventKM is not a valid handle");
+               eError = PVRSRV_ERROR_INVALID_PARAMS;
+       }
+
+       return eError;
+}
+
+IMG_BOOL OSProcHasPrivSrvInit(void)
+{
+       return capable(CAP_SYS_MODULE) != 0;
+}
+
+enum PVRSRV_ERROR OSCopyToUser(void *pvProcess, void __user *pvDest,
+                              const void *pvSrc, u32 ui32Bytes)
+{
+       PVR_UNREFERENCED_PARAMETER(pvProcess);
+
+       if (copy_to_user(pvDest, pvSrc, ui32Bytes) == 0)
+               return PVRSRV_OK;
+       else
+               return PVRSRV_ERROR_GENERIC;
+}
+
+enum PVRSRV_ERROR OSCopyFromUser(void *pvProcess, void *pvDest,
+                                const void __user *pvSrc, u32 ui32Bytes)
+{
+       PVR_UNREFERENCED_PARAMETER(pvProcess);
+
+       if (copy_from_user(pvDest, pvSrc, ui32Bytes) == 0)
+               return PVRSRV_OK;
+       else
+               return PVRSRV_ERROR_GENERIC;
+}
+
+IMG_BOOL OSAccessOK(enum IMG_VERIFY_TEST eVerification,
+                   const void __user *pvUserPtr, u32 ui32Bytes)
+{
+       int linuxType;
+
+       if (eVerification == PVR_VERIFY_READ)
+               linuxType = VERIFY_READ;
+       else if (eVerification == PVR_VERIFY_WRITE)
+               linuxType = VERIFY_WRITE;
+       else {
+               PVR_DPF(PVR_DBG_ERROR, "%s: Unknown eVerification", __func__);
+               return PVRSRV_ERROR_GENERIC;
+       }
+       return (IMG_BOOL)access_ok(linuxType, pvUserPtr, ui32Bytes);
+}
+
+enum eWrapMemType {
+       WRAP_TYPE_CLEANUP,
+       WRAP_TYPE_GET_USER_PAGES,
+       WRAP_TYPE_FIND_VMA_PAGES,
+       WRAP_TYPE_FIND_VMA_PFN
+};
+
+struct sWrapMemInfo {
+       enum eWrapMemType eType;
+       int iNumPages;
+       struct page **ppsPages;
+       struct IMG_SYS_PHYADDR *psPhysAddr;
+       int iPageOffset;
+       int iContiguous;
+#if defined(DEBUG)
+       unsigned long ulStartAddr;
+       unsigned long ulBeyondEndAddr;
+       struct vm_area_struct *psVMArea;
+#endif
+};
+
+static void CheckPagesContiguous(struct sWrapMemInfo *psInfo)
+{
+       unsigned ui;
+       u32 ui32AddrChk;
+
+       BUG_ON(psInfo == NULL);
+
+       psInfo->iContiguous = 1;
+
+       for (ui = 0, ui32AddrChk = psInfo->psPhysAddr[0].uiAddr;
+            ui < psInfo->iNumPages; ui++, ui32AddrChk += PAGE_SIZE)
+               if (psInfo->psPhysAddr[ui].uiAddr != ui32AddrChk) {
+                       psInfo->iContiguous = 0;
+                       break;
+               }
+}
+
+static struct page *CPUVAddrToPage(struct vm_area_struct *psVMArea,
+                                  unsigned long ulCPUVAddr)
+{
+       pgd_t *psPGD;
+       pud_t *psPUD;
+       pmd_t *psPMD;
+       pte_t *psPTE;
+       struct mm_struct *psMM = psVMArea->vm_mm;
+       unsigned long ulPFN;
+       spinlock_t *psPTLock;
+       struct page *psPage;
+
+       psPGD = pgd_offset(psMM, ulCPUVAddr);
+       if (pgd_none(*psPGD) || pgd_bad(*psPGD))
+               return NULL;
+
+       psPUD = pud_offset(psPGD, ulCPUVAddr);
+       if (pud_none(*psPUD) || pud_bad(*psPUD))
+               return NULL;
+
+       psPMD = pmd_offset(psPUD, ulCPUVAddr);
+       if (pmd_none(*psPMD) || pmd_bad(*psPMD))
+               return NULL;
+
+       psPage = NULL;
+
+       psPTE = pte_offset_map_lock(psMM, psPMD, ulCPUVAddr, &psPTLock);
+       if (pte_none(*psPTE) || !pte_present(*psPTE) || !pte_write(*psPTE))
+               goto exit_unlock;
+
+       ulPFN = pte_pfn(*psPTE);
+       if (!pfn_valid(ulPFN))
+               goto exit_unlock;
+
+       psPage = pfn_to_page(ulPFN);
+
+       get_page(psPage);
+
+exit_unlock:
+       pte_unmap_unlock(psPTE, psPTLock);
+
+       return psPage;
+}
+
+enum PVRSRV_ERROR OSReleasePhysPageAddr(void *hOSWrapMem, IMG_BOOL bUseLock)
+{
+       struct sWrapMemInfo *psInfo = (struct sWrapMemInfo *)hOSWrapMem;
+       unsigned ui;
+
+       BUG_ON(psInfo == NULL);
+
+#if defined(DEBUG)
+       switch (psInfo->eType) {
+       case WRAP_TYPE_FIND_VMA_PAGES:
+
+       case WRAP_TYPE_FIND_VMA_PFN:
+               {
+                       struct vm_area_struct *psVMArea;
+
+                       if (bUseLock)
+                               down_read(&current->mm->mmap_sem);
+
+                       psVMArea = find_vma(current->mm, psInfo->ulStartAddr);
+                       if (psVMArea == NULL) {
+                               printk(KERN_WARNING ": OSCpuVToPageListRelease:"
+                " Couldn't find memory region containing start address %lx",
+                psInfo->ulStartAddr);
+
+                               if (bUseLock)
+                                       up_read(&current->mm->mmap_sem);
+
+                               break;
+                       }
+
+                       if (psInfo->psVMArea != psVMArea)
+                               printk(KERN_WARNING ": OSCpuVToPageListRelease:"
+                                       " vm_area_struct has a different "
+                                       "address from the one used in "
+                                       "ImportMem (%p != %p)",
+                                      psVMArea, psInfo->psVMArea);
+
+                       if (psInfo->ulStartAddr < psVMArea->vm_start)
+                               printk(KERN_WARNING ": OSCpuVToPageListRelease:"
+                                       " Start address %lx is outside of "
+                                       "the region returned by find_vma",
+                                      psInfo->ulStartAddr);
+
+                       if (psInfo->ulBeyondEndAddr > psVMArea->vm_end)
+                               printk(KERN_WARNING ": OSCpuVToPageListRelease:"
+                                       " End address %lx is outside of the "
+                                       "region returned by find_vma",
+                                      psInfo->ulBeyondEndAddr);
+
+                       if ((psVMArea->vm_flags & (VM_IO | VM_RESERVED)) !=
+                           (VM_IO | VM_RESERVED))
+                               printk(KERN_WARNING ": OSCpuVToPageListRelease:"
+                                       " Memory region does not represent "
+                                       "memory mapped I/O (VMA flags: 0x%lx)",
+                                      psVMArea->vm_flags);
+
+                       if ((psVMArea->vm_flags & (VM_READ | VM_WRITE)) !=
+                           (VM_READ | VM_WRITE))
+                               printk(KERN_WARNING ": OSCpuVToPageListRelease:"
+                                       " OSWrapMemReleasePages: "
+                                       "No read/write access to memory region "
+                                       "(VMA flags: 0x%lx)",
+                                      psVMArea->vm_flags);
+
+                       if (bUseLock)
+                               up_read(&current->mm->mmap_sem);
+
+                       break;
+               }
+       default:
+               break;
+       }
+#endif
+
+       switch (psInfo->eType) {
+       case WRAP_TYPE_CLEANUP:
+               break;
+       case WRAP_TYPE_FIND_VMA_PFN:
+               break;
+       case WRAP_TYPE_GET_USER_PAGES:
+               {
+                       for (ui = 0; ui < psInfo->iNumPages; ui++) {
+                               struct page *psPage = psInfo->ppsPages[ui];
+
+                               if (!PageReserved(psPage))
+                                       ;
+                               {
+                                       SetPageDirty(psPage);
+                               }
+                               page_cache_release(psPage);
+                       }
+                       break;
+               }
+       case WRAP_TYPE_FIND_VMA_PAGES:
+               {
+                       for (ui = 0; ui < psInfo->iNumPages; ui++)
+                               put_page_testzero(psInfo->ppsPages[ui]);
+                       break;
+               }
+       default:
+               {
+                       printk(KERN_WARNING ": OSCpuVToPageListRelease: "
+                              "Unknown wrap type (%d)", psInfo->eType);
+                       return PVRSRV_ERROR_GENERIC;
+               }
+       }
+
+       if (psInfo->ppsPages != NULL)
+               kfree(psInfo->ppsPages);
+
+       if (psInfo->psPhysAddr != NULL)
+               kfree(psInfo->psPhysAddr);
+
+       kfree(psInfo);
+
+       return PVRSRV_OK;
+}
+
+enum PVRSRV_ERROR OSAcquirePhysPageAddr(void *pvCPUVAddr,
+                                  u32 ui32Bytes,
+                                  struct IMG_SYS_PHYADDR *psSysPAddr,
+                                  void **phOSWrapMem,
+                                  IMG_BOOL bUseLock)
+{
+       unsigned long ulStartAddrOrig = (unsigned long)pvCPUVAddr;
+       unsigned long ulAddrRangeOrig = (unsigned long)ui32Bytes;
+       unsigned long ulBeyondEndAddrOrig = ulStartAddrOrig + ulAddrRangeOrig;
+       unsigned long ulStartAddr;
+       unsigned long ulAddrRange;
+       unsigned long ulBeyondEndAddr;
+       unsigned long ulAddr;
+       int iNumPagesMapped;
+       unsigned ui;
+       struct vm_area_struct *psVMArea;
+       struct sWrapMemInfo *psInfo;
+
+       ulStartAddr = ulStartAddrOrig & PAGE_MASK;
+       ulBeyondEndAddr = PAGE_ALIGN(ulBeyondEndAddrOrig);
+       ulAddrRange = ulBeyondEndAddr - ulStartAddr;
+
+       psInfo = kmalloc(sizeof(*psInfo), GFP_KERNEL);
+       if (psInfo == NULL) {
+               printk(KERN_WARNING ": OSCpuVToPageList: "
+                               "Couldn't allocate information structure\n");
+               return PVRSRV_ERROR_OUT_OF_MEMORY;
+       }
+       memset(psInfo, 0, sizeof(*psInfo));
+
+#if defined(DEBUG)
+       psInfo->ulStartAddr = ulStartAddrOrig;
+       psInfo->ulBeyondEndAddr = ulBeyondEndAddrOrig;
+#endif
+
+       psInfo->iNumPages = ulAddrRange >> PAGE_SHIFT;
+       psInfo->iPageOffset = ulStartAddrOrig & ~PAGE_MASK;
+
+       psInfo->psPhysAddr =
+           kmalloc(psInfo->iNumPages * sizeof(*psInfo->psPhysAddr),
+                   GFP_KERNEL);
+       if (psInfo->psPhysAddr == NULL) {
+               printk(KERN_WARNING
+                      ": OSCpuVToPageList: Couldn't allocate page array\n");
+               goto error_free;
+       }
+
+       psInfo->ppsPages =
+           kmalloc(psInfo->iNumPages * sizeof(*psInfo->ppsPages), GFP_KERNEL);
+       if (psInfo->ppsPages == NULL) {
+               printk(KERN_WARNING
+                      ": OSCpuVToPageList: Couldn't allocate page array\n");
+               goto error_free;
+       }
+
+       if (bUseLock)
+               down_read(&current->mm->mmap_sem);
+
+       iNumPagesMapped =
+           get_user_pages(current, current->mm, ulStartAddr, psInfo->iNumPages,
+                          1, 0, psInfo->ppsPages, NULL);
+
+       if (bUseLock)
+               up_read(&current->mm->mmap_sem);
+
+
+       if (iNumPagesMapped >= 0) {
+
+               if (iNumPagesMapped != psInfo->iNumPages) {
+                       printk(KERN_WARNING ": OSCpuVToPageList: Couldn't "
+                               "map all the pages needed "
+                               "(wanted: %d, got %d \n)",
+                              psInfo->iNumPages, iNumPagesMapped);
+
+                       for (ui = 0; ui < iNumPagesMapped; ui++)
+                               page_cache_release(psInfo->ppsPages[ui]);
+
+                       goto error_free;
+               }
+
+               for (ui = 0; ui < psInfo->iNumPages; ui++) {
+                       struct IMG_CPU_PHYADDR CPUPhysAddr;
+
+                       CPUPhysAddr.uiAddr =
+                           page_to_pfn(psInfo->ppsPages[ui]) << PAGE_SHIFT;
+                       psInfo->psPhysAddr[ui] =
+                           SysCpuPAddrToSysPAddr(CPUPhysAddr);
+                       psSysPAddr[ui] = psInfo->psPhysAddr[ui];
+
+               }
+
+               psInfo->eType = WRAP_TYPE_GET_USER_PAGES;
+
+               goto exit_check;
+       }
+
+       printk(KERN_WARNING ": OSCpuVToPageList: get_user_pages failed (%d), "
+                           "trying something else \n",
+                           iNumPagesMapped);
+
+       if (bUseLock)
+               down_read(&current->mm->mmap_sem);
+
+       psVMArea = find_vma(current->mm, ulStartAddrOrig);
+       if (psVMArea == NULL) {
+               printk(KERN_WARNING ": OSCpuVToPageList: "
+                               "Couldn't find memory region containing "
+                               "start address %lx \n",
+                      ulStartAddrOrig);
+
+               goto error_release_mmap_sem;
+       }
+#if defined(DEBUG)
+       psInfo->psVMArea = psVMArea;
+#endif
+
+       if (ulStartAddrOrig < psVMArea->vm_start) {
+               printk(KERN_WARNING ": OSCpuVToPageList: "
+                               "Start address %lx is outside of the "
+                               "region returned by find_vma\n",
+                      ulStartAddrOrig);
+               goto error_release_mmap_sem;
+       }
+
+       if (ulBeyondEndAddrOrig > psVMArea->vm_end) {
+               printk(KERN_WARNING ": OSCpuVToPageList: "
+                               "End address %lx is outside of the "
+                               "region returned by find_vma\n",
+                      ulBeyondEndAddrOrig);
+               goto error_release_mmap_sem;
+       }
+
+       if ((psVMArea->vm_flags & (VM_IO | VM_RESERVED)) !=
+           (VM_IO | VM_RESERVED)) {
+               printk(KERN_WARNING ": OSCpuVToPageList: "
+                               "Memory region does not represent "
+                               "memory mapped I/O (VMA flags: 0x%lx)\n",
+                      psVMArea->vm_flags);
+               goto error_release_mmap_sem;
+       }
+
+       if ((psVMArea->vm_flags & (VM_READ | VM_WRITE)) !=
+           (VM_READ | VM_WRITE)) {
+               printk(KERN_WARNING ": OSCpuVToPageList: "
+                       "No read/write access to memory region "
+                       "(VMA flags: 0x%lx)\n",
+                      psVMArea->vm_flags);
+               goto error_release_mmap_sem;
+       }
+
+       for (ulAddr = ulStartAddrOrig, ui = 0; ulAddr < ulBeyondEndAddrOrig;
+            ulAddr += PAGE_SIZE, ui++) {
+               struct page *psPage;
+
+               BUG_ON(ui >= psInfo->iNumPages);
+
+               psPage = CPUVAddrToPage(psVMArea, ulAddr);
+               if (psPage == NULL) {
+                       unsigned uj;
+
+                       printk(KERN_WARNING ": OSCpuVToPageList: "
+                               "Couldn't lookup page structure for "
+                               "address 0x%lx, trying something else\n",
+                              ulAddr);
+
+                       for (uj = 0; uj < ui; uj++)
+                               put_page_testzero(psInfo->ppsPages[uj]);
+                       break;
+               }
+
+               psInfo->ppsPages[ui] = psPage;
+       }
+
+       BUG_ON(ui > psInfo->iNumPages);
+       if (ui == psInfo->iNumPages) {
+
+               for (ui = 0; ui < psInfo->iNumPages; ui++) {
+                       struct page *psPage = psInfo->ppsPages[ui];
+                       struct IMG_CPU_PHYADDR CPUPhysAddr;
+
+                       CPUPhysAddr.uiAddr = page_to_pfn(psPage) << PAGE_SHIFT;
+
+                       psInfo->psPhysAddr[ui] =
+                           SysCpuPAddrToSysPAddr(CPUPhysAddr);
+                       psSysPAddr[ui] = psInfo->psPhysAddr[ui];
+               }
+
+               psInfo->eType = WRAP_TYPE_FIND_VMA_PAGES;
+       } else {
+
+               if ((psVMArea->vm_flags & VM_PFNMAP) == 0) {
+                       printk(KERN_WARNING ": OSCpuVToPageList: "
+                              "Region isn't a raw PFN mapping.  Giving up.\n");
+                       goto error_release_mmap_sem;
+               }
+
+               for (ulAddr = ulStartAddrOrig, ui = 0;
+                    ulAddr < ulBeyondEndAddrOrig; ulAddr += PAGE_SIZE, ui++) {
+                       struct IMG_CPU_PHYADDR CPUPhysAddr;
+
+                       CPUPhysAddr.uiAddr =
+                           ((ulAddr - psVMArea->vm_start) +
+                            (psVMArea->vm_pgoff << PAGE_SHIFT)) & PAGE_MASK;
+
+                       psInfo->psPhysAddr[ui] =
+                           SysCpuPAddrToSysPAddr(CPUPhysAddr);
+                       psSysPAddr[ui] = psInfo->psPhysAddr[ui];
+               }
+               BUG_ON(ui != psInfo->iNumPages);
+
+               psInfo->eType = WRAP_TYPE_FIND_VMA_PFN;
+
+               printk(KERN_WARNING
+                      ": OSCpuVToPageList: Region can't be locked down\n");
+       }
+
+       if (bUseLock)
+               up_read(&current->mm->mmap_sem);
+
+exit_check:
+       CheckPagesContiguous(psInfo);
+
+       *phOSWrapMem = (void *) psInfo;
+
+       return PVRSRV_OK;
+
+error_release_mmap_sem:
+       if (bUseLock)
+               up_read(&current->mm->mmap_sem);
+
+error_free:
+       psInfo->eType = WRAP_TYPE_CLEANUP;
+       OSReleasePhysPageAddr((void *) psInfo, bUseLock);
+       return PVRSRV_ERROR_GENERIC;
+}