static gceSTATUS _gc_gather_infomation(char *buf, ssize_t* length)
{
    gceSTATUS status = gcvSTATUS_OK;
    ssize_t len = 0;
    gctUINT32 pid = 0;

    /* #################### [START ==DO NOT CHANGE THE FIRST LINE== START] #################### */
    /* @Ziyi: This string is checked by skia-neon related code to identify Marvell silicon,
              please do not change it and always keep it at the first line of /proc/driver/gc ! */
    gckOS_GetProcessID(&pid);
    len += sprintf(buf+len, "[%3d]%s(%s)\n", pid, _VENDOR_STRING_, _GC_VERSION_STRING_);
    /* @Ziyi: If any change happened between these 2 comments please contact [email protected], Thanks. */
    /* #################### [END ====DO NOT CHANGE THE FIRST LINE==== END] #################### */

    if(1)
    {
        gctUINT32 tmpLen = 0;
        gcmkONERROR(gckOS_ShowVidMemUsage(galDevice->os, buf+len, &tmpLen));
        len += tmpLen;
    }

    *length = len;
    return gcvSTATUS_OK;

OnError:
    return status;
}
/*******************************************************************************
**
**  gckKERNEL_Dispatch
**
**  Dispatch a command received from the user HAL layer.
**
**  INPUT:
**
**      gckKERNEL Kernel
**          Pointer to an gckKERNEL object.
**
**      gcsHAL_INTERFACE * Interface
**          Pointer to a gcsHAL_INTERFACE structure that defines the command to
**          be dispatched.
**
**  OUTPUT:
**
**      gcsHAL_INTERFACE * Interface
**          Pointer to a gcsHAL_INTERFACE structure that receives any data to be
**          returned.
*/
gceSTATUS gckVGKERNEL_Dispatch(
    IN gckKERNEL Kernel,
    IN gctBOOL FromUser,
    IN OUT gcsHAL_INTERFACE * Interface
    )
{
    gceSTATUS status;
    gcsHAL_INTERFACE * kernelInterface = Interface;
    gcuVIDMEM_NODE_PTR node;
    gctUINT32 processID;

    gcmkHEADER_ARG("Kernel=0x%x Interface=0x%x ", Kernel, Interface);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
    gcmkVERIFY_ARGUMENT(Interface != gcvNULL);

    gcmkONERROR(gckOS_GetProcessID(&processID));

    /* Dispatch on command. */
    switch (Interface->command)
    {
    case gcvHAL_QUERY_VIDEO_MEMORY:
        /* Query video memory size. */
        gcmkERR_BREAK(gckKERNEL_QueryVideoMemory(
            Kernel, kernelInterface
            ));
        break;

    case gcvHAL_QUERY_CHIP_IDENTITY:
        /* Query chip identity. */
        gcmkERR_BREAK(gckVGHARDWARE_QueryChipIdentity(
            Kernel->vg->hardware,
            &kernelInterface->u.QueryChipIdentity.chipModel,
            &kernelInterface->u.QueryChipIdentity.chipRevision,
            &kernelInterface->u.QueryChipIdentity.chipFeatures,
            &kernelInterface->u.QueryChipIdentity.chipMinorFeatures,
            &kernelInterface->u.QueryChipIdentity.chipMinorFeatures2
            ));
        break;

    case gcvHAL_QUERY_COMMAND_BUFFER:
        /* Query command buffer information. */
        gcmkERR_BREAK(gckKERNEL_QueryCommandBuffer(
            Kernel,
            &kernelInterface->u.QueryCommandBuffer.information
            ));
        break;
    case gcvHAL_ALLOCATE_NON_PAGED_MEMORY:
        /* Allocate non-paged memory. */
        gcmkERR_BREAK(gckOS_AllocateContiguous(
            Kernel->os,
            gcvTRUE,
            &kernelInterface->u.AllocateNonPagedMemory.bytes,
            &kernelInterface->u.AllocateNonPagedMemory.physical,
            &kernelInterface->u.AllocateNonPagedMemory.logical
            ));
        break;

    case gcvHAL_FREE_NON_PAGED_MEMORY:
        /* Unmap user logical out of physical memory first. */
        gcmkERR_BREAK(gckOS_UnmapUserLogical(
            Kernel->os,
            kernelInterface->u.AllocateNonPagedMemory.physical,
            kernelInterface->u.AllocateNonPagedMemory.bytes,
            kernelInterface->u.AllocateNonPagedMemory.logical
            ));

        /* Free non-paged memory. */
        gcmkERR_BREAK(gckOS_FreeNonPagedMemory(
            Kernel->os,
            kernelInterface->u.AllocateNonPagedMemory.bytes,
            kernelInterface->u.AllocateNonPagedMemory.physical,
            kernelInterface->u.AllocateNonPagedMemory.logical
            ));
        break;

    case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY:
        /* Allocate contiguous memory. */
        gcmkERR_BREAK(gckOS_AllocateContiguous(
            Kernel->os,
            gcvTRUE,
            &kernelInterface->u.AllocateNonPagedMemory.bytes,
            &kernelInterface->u.AllocateNonPagedMemory.physical,
            &kernelInterface->u.AllocateNonPagedMemory.logical
            ));
        break;

    case gcvHAL_FREE_CONTIGUOUS_MEMORY:
        /* Unmap user logical out of physical memory first. */
        gcmkERR_BREAK(gckOS_UnmapUserLogical(
            Kernel->os,
            kernelInterface->u.AllocateNonPagedMemory.physical,
            kernelInterface->u.AllocateNonPagedMemory.bytes,
            kernelInterface->u.AllocateNonPagedMemory.logical
            ));

        /* Free contiguous memory. */
        gcmkERR_BREAK(gckOS_FreeContiguous(
            Kernel->os,
            kernelInterface->u.AllocateNonPagedMemory.physical,
            kernelInterface->u.AllocateNonPagedMemory.logical,
            kernelInterface->u.AllocateNonPagedMemory.bytes
            ));
        break;

    case gcvHAL_ALLOCATE_VIDEO_MEMORY:
        {
            gctSIZE_T bytes;
            gctUINT32 bitsPerPixel;
            gctUINT32 bits;

            /* Align width and height to tiles. */
            gcmkERR_BREAK(gckVGHARDWARE_AlignToTile(
                Kernel->vg->hardware,
                kernelInterface->u.AllocateVideoMemory.type,
                &kernelInterface->u.AllocateVideoMemory.width,
                &kernelInterface->u.AllocateVideoMemory.height
                ));

            /* Convert format into bytes per pixel and bytes per tile. */
            gcmkERR_BREAK(gckVGHARDWARE_ConvertFormat(
                Kernel->vg->hardware,
                kernelInterface->u.AllocateVideoMemory.format,
                &bitsPerPixel,
                gcvNULL
                ));

            /* Compute number of bits for the allocation. */
            bits
                = kernelInterface->u.AllocateVideoMemory.width
                * kernelInterface->u.AllocateVideoMemory.height
                * kernelInterface->u.AllocateVideoMemory.depth
                * bitsPerPixel;

            /* Compute number of bytes for the allocation. */
            bytes = gcmALIGN(bits, 8) / 8;

            /* Allocate memory. */
            gcmkERR_BREAK(gckKERNEL_AllocateLinearMemory(
                Kernel,
                &kernelInterface->u.AllocateVideoMemory.pool,
                bytes,
                64,
                kernelInterface->u.AllocateVideoMemory.type,
                &kernelInterface->u.AllocateVideoMemory.node
                ));
        }
        break;

    case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY:
        /* Allocate memory. */
        gcmkERR_BREAK(gckKERNEL_AllocateLinearMemory(
            Kernel,
            &kernelInterface->u.AllocateLinearVideoMemory.pool,
            kernelInterface->u.AllocateLinearVideoMemory.bytes,
            kernelInterface->u.AllocateLinearVideoMemory.alignment,
            kernelInterface->u.AllocateLinearVideoMemory.type,
            &kernelInterface->u.AllocateLinearVideoMemory.node
            ));

        gcmkERR_BREAK(gckKERNEL_AddProcessDB(Kernel,
           processID, gcvDB_VIDEO_MEMORY,
           Interface->u.AllocateLinearVideoMemory.node,
           gcvNULL,
           kernelInterface->u.AllocateLinearVideoMemory.bytes
           ));

        break;

    case gcvHAL_FREE_VIDEO_MEMORY:
#ifdef __QNXNTO__
        /* Unmap the video memory */
        node = Interface->u.FreeVideoMemory.node;

        if ((node->VidMem.memory->object.type == gcvOBJ_VIDMEM) &&
            (node->VidMem.logical != gcvNULL))
        {
            gckKERNEL_UnmapVideoMemory(Kernel,
                                       node->VidMem.logical,
                                       processID,
                                       node->VidMem.bytes);
            node->VidMem.logical = gcvNULL;
        }
#endif /* __QNXNTO__ */

        /* Free video memory. */
        gcmkERR_BREAK(gckVIDMEM_Free(
            Interface->u.FreeVideoMemory.node
            ));

        gcmkERR_BREAK(gckKERNEL_RemoveProcessDB(
            Kernel,
            processID, gcvDB_VIDEO_MEMORY,
            Interface->u.FreeVideoMemory.node
            ));

        break;

    case gcvHAL_MAP_MEMORY:
        /* Map memory. */
        gcmkERR_BREAK(gckKERNEL_MapMemory(
            Kernel,
            kernelInterface->u.MapMemory.physical,
            kernelInterface->u.MapMemory.bytes,
            &kernelInterface->u.MapMemory.logical
            ));
        break;

    case gcvHAL_UNMAP_MEMORY:
        /* Unmap memory. */
        gcmkERR_BREAK(gckKERNEL_UnmapMemory(
            Kernel,
            kernelInterface->u.MapMemory.physical,
            kernelInterface->u.MapMemory.bytes,
            kernelInterface->u.MapMemory.logical
            ));
        break;

    case gcvHAL_MAP_USER_MEMORY:
        /* Map user memory to DMA. */
        gcmkERR_BREAK(gckOS_MapUserMemory(
            Kernel->os,
            gcvCORE_VG,
            kernelInterface->u.MapUserMemory.memory,
            kernelInterface->u.MapUserMemory.physical,
            kernelInterface->u.MapUserMemory.size,
            &kernelInterface->u.MapUserMemory.info,
            &kernelInterface->u.MapUserMemory.address
            ));
        break;

    case gcvHAL_UNMAP_USER_MEMORY:
        /* Unmap user memory. */
        gcmkERR_BREAK(gckOS_UnmapUserMemory(
            Kernel->os,
            gcvCORE_VG,
            kernelInterface->u.UnmapUserMemory.memory,
            kernelInterface->u.UnmapUserMemory.size,
            kernelInterface->u.UnmapUserMemory.info,
            kernelInterface->u.UnmapUserMemory.address
            ));
        break;
    case gcvHAL_LOCK_VIDEO_MEMORY:
        /* Lock video memory. */
        gcmkERR_BREAK(
            gckVIDMEM_Lock(Kernel,
                           Interface->u.LockVideoMemory.node,
                           gcvFALSE,
                           &Interface->u.LockVideoMemory.address));

        node = Interface->u.LockVideoMemory.node;
        if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
        {
            /* Map video memory address into user space. */
#ifdef __QNXNTO__
        if (node->VidMem.logical == gcvNULL)
        {
            gcmkONERROR(
                gckKERNEL_MapVideoMemory(Kernel,
                                         FromUser,
                                         Interface->u.LockVideoMemory.address,
                                         processID,
                                         node->VidMem.bytes,
                                         &node->VidMem.logical));
        }

        Interface->u.LockVideoMemory.memory = node->VidMem.logical;
#else
            gcmkERR_BREAK(
                gckKERNEL_MapVideoMemoryEx(Kernel,
                                         gcvCORE_VG,
                                         FromUser,
                                         Interface->u.LockVideoMemory.address,
                                         &Interface->u.LockVideoMemory.memory));
#endif
        }
        else
        {
            Interface->u.LockVideoMemory.memory = node->Virtual.logical;

            /* Success. */
            status = gcvSTATUS_OK;
        }

#if gcdSECURE_USER
        /* Return logical address as physical address. */
        Interface->u.LockVideoMemory.address =
            gcmPTR2INT(Interface->u.LockVideoMemory.memory);
#endif
        gcmkERR_BREAK(
            gckKERNEL_AddProcessDB(Kernel,
                                   processID, gcvDB_VIDEO_MEMORY_LOCKED,
                                   Interface->u.LockVideoMemory.node,
                                   gcvNULL,
                                   0));
        break;

    case gcvHAL_UNLOCK_VIDEO_MEMORY:
        /* Unlock video memory. */
        node = Interface->u.UnlockVideoMemory.node;

#if gcdSECURE_USER
        /* Save node information before it disappears. */
        if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
        {
            logical = gcvNULL;
            bytes   = 0;
        }
        else
        {
            logical = node->Virtual.logical;
            bytes   = node->Virtual.bytes;
        }
#endif

        /* Unlock video memory. */
        gcmkERR_BREAK(
            gckVIDMEM_Unlock(Kernel,
                             node,
                             Interface->u.UnlockVideoMemory.type,
                             &Interface->u.UnlockVideoMemory.asynchroneous,
                             gcvFALSE));

#if gcdSECURE_USER
        /* Flush the translation cache for virtual surfaces. */
        if (logical != gcvNULL)
        {
            gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(Kernel,
                                                          cache,
                                                          logical,
                                                          bytes));
        }
#endif

        if (Interface->u.UnlockVideoMemory.asynchroneous == gcvFALSE)
        {
            /* There isn't a event to unlock this node, remove record now */
            gcmkERR_BREAK(
                    gckKERNEL_RemoveProcessDB(Kernel,
                        processID, gcvDB_VIDEO_MEMORY_LOCKED,
                        Interface->u.UnlockVideoMemory.node));
        }

        break;
    case gcvHAL_USER_SIGNAL:
#if !USE_NEW_LINUX_SIGNAL
        /* Dispatch depends on the user signal subcommands. */
        switch(Interface->u.UserSignal.command)
        {
        case gcvUSER_SIGNAL_CREATE:
            /* Create a signal used in the user space. */
            gcmkERR_BREAK(
                gckOS_CreateUserSignal(Kernel->os,
                                       Interface->u.UserSignal.manualReset,
                                       &Interface->u.UserSignal.id));

            gcmkVERIFY_OK(
                gckKERNEL_AddProcessDB(Kernel,
                                       processID, gcvDB_SIGNAL,
                                       gcmINT2PTR(Interface->u.UserSignal.id),
                                       gcvNULL,
                                       0));
            break;

        case gcvUSER_SIGNAL_DESTROY:
            /* Destroy the signal. */
            gcmkERR_BREAK(
                gckOS_DestroyUserSignal(Kernel->os,
                                        Interface->u.UserSignal.id));

            gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
                Kernel,
                processID, gcvDB_SIGNAL,
                gcmINT2PTR(Interface->u.UserSignal.id)));
            break;

        case gcvUSER_SIGNAL_SIGNAL:
            /* Signal the signal. */
            gcmkERR_BREAK(
                gckOS_SignalUserSignal(Kernel->os,
                                       Interface->u.UserSignal.id,
                                       Interface->u.UserSignal.state));
            break;

        case gcvUSER_SIGNAL_WAIT:
            /* Wait on the signal. */
            status = gckOS_WaitUserSignal(Kernel->os,
                                          Interface->u.UserSignal.id,
                                          Interface->u.UserSignal.wait);
            break;

        default:
            /* Invalid user signal command. */
            gcmkERR_BREAK(gcvSTATUS_INVALID_ARGUMENT);
        }
#endif
        break;

    case gcvHAL_COMMIT:
        /* Commit a command and context buffer. */
        gcmkERR_BREAK(gckVGCOMMAND_Commit(
            Kernel->vg->command,
            kernelInterface->u.VGCommit.context,
            kernelInterface->u.VGCommit.queue,
            kernelInterface->u.VGCommit.entryCount,
            kernelInterface->u.VGCommit.taskTable
            ));
        break;
    case gcvHAL_VERSION:
        kernelInterface->u.Version.major = gcvVERSION_MAJOR;
        kernelInterface->u.Version.minor = gcvVERSION_MINOR;
        kernelInterface->u.Version.patch = gcvVERSION_PATCH;
        kernelInterface->u.Version.build = gcvVERSION_BUILD;
        status = gcvSTATUS_OK;
        break;

    case gcvHAL_GET_BASE_ADDRESS:
        /* Get base address. */
        gcmkERR_BREAK(
            gckOS_GetBaseAddress(Kernel->os,
                                 &kernelInterface->u.GetBaseAddress.baseAddress));
        break;
    default:
        /* Invalid command. */
        status = gcvSTATUS_INVALID_ARGUMENT;
    }

OnError:
    /* Save status. */
    kernelInterface->status = status;

    gcmkFOOTER();

    /* Return the status. */
    return status;
}
long drv_ioctl(
    struct file* filp,
    unsigned int ioctlCode,
    unsigned long arg
    )
{
    gceSTATUS status;
    gcsHAL_INTERFACE iface;
    gctUINT32 copyLen;
    DRIVER_ARGS drvArgs;
    gckGALDEVICE device;
    gcsHAL_PRIVATE_DATA_PTR data;
    gctINT32 i, count;
    gckVIDMEM_NODE nodeObject;

    gcmkHEADER_ARG(
        "filp=0x%08X ioctlCode=0x%08X arg=0x%08X",
        filp, ioctlCode, arg
        );

    if (filp == gcvNULL)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): filp is NULL\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    data = filp->private_data;

    if (data == gcvNULL)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): private_data is NULL\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    device = data->device;

    if (device == gcvNULL)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): device is NULL\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    if ((ioctlCode != IOCTL_GCHAL_INTERFACE)
    &&  (ioctlCode != IOCTL_GCHAL_KERNEL_INTERFACE)
    )
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): unknown command %d\n",
            __FUNCTION__, __LINE__,
            ioctlCode
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    /* Get the drvArgs. */
    copyLen = copy_from_user(
        &drvArgs, (void *) arg, sizeof(DRIVER_ARGS)
        );

    if (copyLen != 0)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): error copying of the input arguments.\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    /* Now bring in the gcsHAL_INTERFACE structure. */
    if ((drvArgs.InputBufferSize  != sizeof(gcsHAL_INTERFACE))
    ||  (drvArgs.OutputBufferSize != sizeof(gcsHAL_INTERFACE))
    )
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): input or/and output structures are invalid.\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    copyLen = copy_from_user(
        &iface, gcmUINT64_TO_PTR(drvArgs.InputBuffer), sizeof(gcsHAL_INTERFACE)
        );

    if (copyLen != 0)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): error copying of input HAL interface.\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    if (iface.command == gcvHAL_CHIP_INFO)
    {
        count = 0;
        for (i = 0; i < gcdMAX_GPU_COUNT; i++)
        {
            if (device->kernels[i] != gcvNULL)
            {
#if gcdENABLE_VG
                if (i == gcvCORE_VG)
                {
                    iface.u.ChipInfo.types[count] = gcvHARDWARE_VG;
                }
                else
#endif
                {
                    gcmkVERIFY_OK(gckHARDWARE_GetType(device->kernels[i]->hardware,
                                                      &iface.u.ChipInfo.types[count]));
                }
                count++;
            }
        }

        iface.u.ChipInfo.count = count;
        iface.status = status = gcvSTATUS_OK;
    }
    else
    {
        if (iface.hardwareType > 7)
        {
            gcmkTRACE_ZONE(
                gcvLEVEL_ERROR, gcvZONE_DRIVER,
                "%s(%d): unknown hardwareType %d\n",
                __FUNCTION__, __LINE__,
                iface.hardwareType
                );

            gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
        }

#if gcdENABLE_VG
        if (device->coreMapping[iface.hardwareType] == gcvCORE_VG)
        {
            status = gckVGKERNEL_Dispatch(device->kernels[gcvCORE_VG],
                                        (ioctlCode == IOCTL_GCHAL_INTERFACE),
                                        &iface);
        }
        else
#endif
        {
            status = gckKERNEL_Dispatch(device->kernels[device->coreMapping[iface.hardwareType]],
                                        (ioctlCode == IOCTL_GCHAL_INTERFACE),
                                        &iface);
        }
    }

    /* Redo system call after pending signal is handled. */
    if (status == gcvSTATUS_INTERRUPTED)
    {
        gcmkFOOTER();
        return -ERESTARTSYS;
    }

    if (gcmIS_SUCCESS(status) && (iface.command == gcvHAL_LOCK_VIDEO_MEMORY))
    {
        gcuVIDMEM_NODE_PTR node;
        gctUINT32 processID;

        gckOS_GetProcessID(&processID);

        gcmkONERROR(gckVIDMEM_HANDLE_Lookup(device->kernels[device->coreMapping[iface.hardwareType]],
                                processID,
                                (gctUINT32)iface.u.LockVideoMemory.node,
                                &nodeObject));
        node = nodeObject->node;

        /* Special case for mapped memory. */
        if ((data->mappedMemory != gcvNULL)
        &&  (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
        )
        {
            /* Compute offset into mapped memory. */
            gctUINT32 offset
                = (gctUINT8 *) gcmUINT64_TO_PTR(iface.u.LockVideoMemory.memory)
                - (gctUINT8 *) device->contiguousBase;

            /* Compute offset into user-mapped region. */
            iface.u.LockVideoMemory.memory =
                gcmPTR_TO_UINT64((gctUINT8 *) data->mappedMemory + offset);
        }
    }

    /* Copy data back to the user. */
    copyLen = copy_to_user(
        gcmUINT64_TO_PTR(drvArgs.OutputBuffer), &iface, sizeof(gcsHAL_INTERFACE)
        );

    if (copyLen != 0)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): error copying of output HAL interface.\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    /* Success. */
    gcmkFOOTER_NO();
    return 0;

OnError:
    gcmkFOOTER();
    return -ENOTTY;
}
int drv_open(
    struct inode* inode,
    struct file* filp
    )
{
    gceSTATUS status;
    gctBOOL attached = gcvFALSE;
    gcsHAL_PRIVATE_DATA_PTR data = gcvNULL;
    gctINT i;

    gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp);

    if (filp == gcvNULL)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): filp is NULL\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    data = kmalloc(sizeof(gcsHAL_PRIVATE_DATA), GFP_KERNEL | __GFP_NOWARN);

    if (data == gcvNULL)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): private_data is NULL\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
    }

    data->device             = galDevice;
    data->mappedMemory       = gcvNULL;
    data->contiguousLogical  = gcvNULL;
    gcmkONERROR(gckOS_GetProcessID(&data->pidOpen));

    /* Attached the process. */
    for (i = 0; i < gcdMAX_GPU_COUNT; i++)
    {
        if (galDevice->kernels[i] != gcvNULL)
        {
            gcmkONERROR(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvTRUE));
        }
    }
    attached = gcvTRUE;

    if (!galDevice->contiguousMapped)
    {
        if (galDevice->contiguousPhysical != gcvNULL)
        {
            gcmkONERROR(gckOS_MapMemory(
                galDevice->os,
                galDevice->contiguousPhysical,
                galDevice->contiguousSize,
                &data->contiguousLogical
                ));
        }
    }

    filp->private_data = data;

    /* Success. */
    gcmkFOOTER_NO();
    return 0;

OnError:
    if (data != gcvNULL)
    {
        if (data->contiguousLogical != gcvNULL)
        {
            gcmkVERIFY_OK(gckOS_UnmapMemory(
                galDevice->os,
                galDevice->contiguousPhysical,
                galDevice->contiguousSize,
                data->contiguousLogical
                ));
        }

        kfree(data);
    }

    if (attached)
    {
        for (i = 0; i < gcdMAX_GPU_COUNT; i++)
        {
            if (galDevice->kernels[i] != gcvNULL)
            {
                gcmkVERIFY_OK(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvFALSE));
            }
        }
    }

    gcmkFOOTER();
    return -ENOTTY;
}
/*******************************************************************************
**
**  gckKERNEL_Recovery
**
**  Try to recover the GPU from a fatal error.
**
**  INPUT:
**
**      gckKERNEL Kernel
**          Pointer to an gckKERNEL object.
**
**  OUTPUT:
**
**      Nothing.
*/
gceSTATUS
gckKERNEL_Recovery(
    IN gckKERNEL Kernel
    )
{
    gceSTATUS status;
    gckEVENT event;
    gckHARDWARE hardware;
#if gcdSECURE_USER
    gctUINT32 processID;
#endif

    gcmkHEADER_ARG("Kernel=0x%x", Kernel);

    /* Validate the arguemnts. */
    gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);

    /* Grab gckEVENT object. */
    event = Kernel->event;
    gcmkVERIFY_OBJECT(event, gcvOBJ_EVENT);

    /* Grab gckHARDWARE object. */
    hardware = Kernel->hardware;
    gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);

    /* Handle all outstanding events now. */
    event->pending = ~0U;
    gcmkONERROR(gckEVENT_Notify(event, 1));

    /* Again in case more events got submitted. */
    event->pending = ~0U;
    gcmkONERROR(gckEVENT_Notify(event, 2));

#if gcdSECURE_USER
    /* Flush the secure mapping cache. */
    gcmkONERROR(gckOS_GetProcessID(&processID));
    gcmkONERROR(gckKERNEL_MapLogicalToPhysical(Kernel, processID, gcvNULL));
#endif

    /* Try issuing a soft reset for the GPU. */
    status = gckHARDWARE_Reset(hardware);
    if (status == gcvSTATUS_NOT_SUPPORTED)
    {
        /* Switch to OFF power.  The next submit should return the GPU to ON
        ** state. */
        gcmkONERROR(
            gckHARDWARE_SetPowerManagementState(hardware,
                                                gcvPOWER_OFF));
    }
    else
    {
        /* Bail out on reset error. */
        gcmkONERROR(status);
    }

    /* Success. */
    gcmkFOOTER_NO();
    return gcvSTATUS_OK;

OnError:
    /* Return the status. */
    gcmkFOOTER();
    return status;
}
int drv_release(
    struct inode* inode,
    struct file* filp
    )
{
    gceSTATUS status;
    gcsHAL_PRIVATE_DATA_PTR data;
    gckGALDEVICE device;
    gctINT i;
    gctUINT32 processID;


    gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp);

    if (filp == gcvNULL)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): filp is NULL\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    data = filp->private_data;

    if (data == gcvNULL)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): private_data is NULL\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    device = data->device;

    if (device == gcvNULL)
    {
        gcmkTRACE_ZONE(
            gcvLEVEL_ERROR, gcvZONE_DRIVER,
            "%s(%d): device is NULL\n",
            __FUNCTION__, __LINE__
            );

        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    if (!device->contiguousMapped)
    {
        if (data->contiguousLogical != gcvNULL)
        {
            gcmkVERIFY_OK(gckOS_GetProcessID(&processID));
            gcmkONERROR(gckOS_UnmapMemoryEx(
                galDevice->os,
                galDevice->contiguousPhysical,
                galDevice->contiguousSize,
                data->contiguousLogical,
                data->pidOpen
                ));

            for (i = 0; i < gcdCORE_COUNT; i++)
            {
                if (galDevice->kernels[i] != gcvNULL)
                {
                    gcmkVERIFY_OK(
                         gckKERNEL_RemoveProcessDB(galDevice->kernels[i],
                                                   processID, gcvDB_MAP_MEMORY,
                                                   data->contiguousLogical));
                }
            }

            data->contiguousLogical = gcvNULL;
        }
    }

    /* Clean user signals if exit unnormally. */
    gcmkVERIFY_OK(gckOS_GetProcessID(&processID));
    gcmkVERIFY_OK(gckOS_CleanProcessSignal(galDevice->os, (gctHANDLE)processID));

    /* A process gets detached. */
    for (i = 0; i < gcdCORE_COUNT; i++)
    {
        if (galDevice->kernels[i] != gcvNULL)
        {
            gcmkONERROR(gckKERNEL_AttachProcessEx(galDevice->kernels[i], gcvFALSE, data->pidOpen));
        }
    }

    kfree(data);
    filp->private_data = NULL;

    /* Success. */
    gcmkFOOTER_NO();
    return 0;

OnError:
    gcmkFOOTER();
    return -ENOTTY;
}
/*******************************************************************************
**
**  gckKERNEL_MapVideoMemory
**
**  Get the logical address for a hardware specific memory address for the
**  current process.
**
**  INPUT:
**
**      gckKERNEL Kernel
**          Pointer to an gckKERNEL object.
**
**      gctBOOL InUserSpace
**          gcvTRUE to map the memory into the user space.
**
**      gctUINT32 Address
**          Hardware specific memory address.
**
**  OUTPUT:
**
**      gctPOINTER * Logical
**          Pointer to a variable that will hold the logical address of the
**          specified memory address.
*/
gceSTATUS
gckKERNEL_MapVideoMemoryEx(
    IN gckKERNEL Kernel,
    IN gceCORE Core,
    IN gctBOOL InUserSpace,
    IN gctUINT32 Address,
    OUT gctPOINTER * Logical
    )
{
    gckGALDEVICE device;
    PLINUX_MDL mdl;
    PLINUX_MDL_MAP mdlMap;
    gcePOOL pool;
    gctUINT32 offset, base;
    gceSTATUS status;
    gctPOINTER logical;

    gcmkHEADER_ARG("Kernel=%p InUserSpace=%d Address=%08x",
                   Kernel, InUserSpace, Address);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
    gcmkVERIFY_ARGUMENT(Logical != NULL);

    /* Extract the pointer to the gckGALDEVICE class. */
    device = (gckGALDEVICE) Kernel->context;

#if gcdENABLE_VG
    if (Core == gcvCORE_VG)
    {
        /* Split the memory address into a pool type and offset. */
        gcmkONERROR(
            gckVGHARDWARE_SplitMemory(Kernel->vg->hardware, Address, &pool, &offset));
    }
    else
#endif
    {
        /* Split the memory address into a pool type and offset. */
        gcmkONERROR(
            gckHARDWARE_SplitMemory(Kernel->hardware, Address, &pool, &offset));
    }

    /* Dispatch on pool. */
    switch (pool)
    {
    case gcvPOOL_LOCAL_INTERNAL:
        /* Internal memory. */
        logical = device->internalLogical;
        break;

    case gcvPOOL_LOCAL_EXTERNAL:
        /* External memory. */
        logical = device->externalLogical;
        break;

    case gcvPOOL_SYSTEM:
        /* System memory. */
        if (device->contiguousMapped)
        {
            logical = device->contiguousBase;
        }
        else
        {
            gctINT processID;
            gckOS_GetProcessID(&processID);

            mdl = (PLINUX_MDL) device->contiguousPhysical;

            mdlMap = FindMdlMap(mdl, processID);
            gcmkASSERT(mdlMap);

            logical = (gctPOINTER) mdlMap->vmaAddr;
        }
#if gcdENABLE_VG
        if (Core == gcvCORE_VG)
        {
            gcmkVERIFY_OK(
                gckVGHARDWARE_SplitMemory(Kernel->vg->hardware,
                                        device->contiguousVidMem->baseAddress,
                                        &pool,
                                        &base));
        }
        else
#endif
        {
            gctUINT32 baseAddress = 0;

            if (Kernel->hardware->mmuVersion == 0)
            {
                gcmkONERROR(gckOS_GetBaseAddress(Kernel->os, &baseAddress));
            }

            gcmkVERIFY_OK(
                gckHARDWARE_SplitMemory(Kernel->hardware,
                                        device->contiguousVidMem->baseAddress - baseAddress,
                                        &pool,
                                        &base));
        }
        offset -= base;
        break;

    default:
        /* Invalid memory pool. */
        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
    }

    /* Build logical address of specified address. */
    *Logical = (gctPOINTER) ((gctUINT8_PTR) logical + offset);

    /* Success. */
    gcmkFOOTER_ARG("*Logical=%p", *Logical);
    return gcvSTATUS_OK;

OnError:
    /* Retunn the status. */
    gcmkFOOTER();
    return status;
}
static ssize_t store_power_state (struct device *dev,
                    struct device_attribute *attr,
                    const char *buf, size_t count)
{
    int state, core, broadcast, gpu_count, i;
    gceSTATUS status = gcvSTATUS_OK;
    gctUINT32 tgid;

    /* count ocre numbers */
    for (i=0, gpu_count = 0; i < gcdMAX_GPU_COUNT; i++)
        if (galDevice->kernels[i] != gcvNULL)
            gpu_count++;

    /* scan input value and verify */
    SYSFS_VERIFY_INPUT(sscanf(buf, "%d,%d,%d", &state, &core, &broadcast), 3);
    SYSFS_VERIFY_INPUT_RANGE(state, 0, 2);
    SYSFS_VERIFY_INPUT_RANGE(core, 0, (gpu_count-1));
    SYSFS_VERIFY_INPUT_RANGE(broadcast, 0, 1);

    gckOS_GetProcessID(&tgid);

    switch (state)
    {
        case 0:
            /* on */
            printk("[pm_test %d] %s core power state on\n", tgid, (core == gcvCORE_MAJOR)?"3D":"2D");
            if (broadcast)
                /* noting done here */
                gcmkONERROR(gckOS_Broadcast(galDevice->kernels[core]->os, galDevice->kernels[core]->hardware, gcvBROADCAST_FIRST_PROCESS));
            else
                gcmkONERROR(gckHARDWARE_SetPowerManagementState(galDevice->kernels[core]->hardware, gcvPOWER_ON));
            printk("[pm_test %d] %s core power state on - done\n", tgid, (core == gcvCORE_MAJOR)?"3D":"2D");
            break;

        case 1:
            /* off */
            printk("[pm_test %d] %s core power state off\n", tgid, (core == gcvCORE_MAJOR)?"3D":"2D");
            if (broadcast)
                gcmkONERROR(gckOS_Broadcast(galDevice->kernels[core]->os, galDevice->kernels[core]->hardware, gcvBROADCAST_LAST_PROCESS));
            else
                gcmkONERROR(gckHARDWARE_SetPowerManagementState(galDevice->kernels[core]->hardware, gcvPOWER_OFF));
            printk("[pm_test %d] %s core power state off - done\n", tgid, (core == gcvCORE_MAJOR)?"3D":"2D");
            break;

        case 2:
            /* suspend */
            printk("[pm_test %d] %s core power state suspend\n", tgid, (core == gcvCORE_MAJOR)?"3D":"2D");
            if (broadcast)
                gcmkONERROR(gckOS_Broadcast(galDevice->kernels[core]->os, galDevice->kernels[core]->hardware, gcvBROADCAST_GPU_IDLE));
            else
                gcmkONERROR(gckHARDWARE_SetPowerManagementState(galDevice->kernels[core]->hardware, gcvPOWER_SUSPEND));
            printk("[pm_test %d] %s core power state suspend - done\n", tgid, (core == gcvCORE_MAJOR)?"3D":"2D");
            break;

        default:
            break;
    }
    return count;

OnError:
    printk("[pm_test %d] %s core power state %s - bail out\n", tgid, (core == gcvCORE_MAJOR)?"3D":"2D", (state==1)?"off":"on");
    return (ssize_t)-EINVAL;

}