/*!
******************************************************************************

 @Function              palloc_fnCompConnect

******************************************************************************/
static IMG_RESULT palloc_fnCompConnect (
    IMG_HANDLE   hAttachHandle,
    IMG_VOID **  ppvCompAttachmentData
)
{
    PALLOC_sAttachContext *  psAttachContext;
    IMG_UINT32               ui32Result;
    IMG_CHAR *               pszDeviceName;

    /* Allocate attachment context structure...*/
    psAttachContext = IMG_MALLOC(sizeof(*psAttachContext));
    IMG_ASSERT(psAttachContext != IMG_NULL);
    if (psAttachContext == IMG_NULL)
    {
        return IMG_ERROR_OUT_OF_MEMORY;
    }
    IMG_MEMSET(psAttachContext, 0, sizeof(*psAttachContext));

    /* Ensure the resource manager is initialised...*/
    ui32Result = RMAN_Initialise();
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_rman_init;
    }

    /* Create a bucket for the resources...*/
    ui32Result = RMAN_CreateBucket(&psAttachContext->hResBHandle);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_rman_bucket;
    }

    /* Get device information...*/
    psAttachContext->hDevHandle = DMANKM_GetDevHandleFromAttach(hAttachHandle);
    pszDeviceName = DMANKM_GetDeviceName(psAttachContext->hDevHandle);
    ui32Result = SYSDEVU_OpenDevice(pszDeviceName, &psAttachContext->hSysDevHandle);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_sysdev_open;
    }

    /* Return attachment context...*/
    *ppvCompAttachmentData = psAttachContext;

    /* Return success...*/
    return IMG_SUCCESS;

    /* Error handling. */
error_sysdev_open:
    RMAN_DestroyBucket(psAttachContext->hResBHandle);
error_rman_bucket:
error_rman_init:
    IMG_FREE(psAttachContext);

    return ui32Result;
}
/*!
******************************************************************************

 @Function              PALLOC_Import1

******************************************************************************/
IMG_RESULT PALLOC_Import1(
    IMG_UINT32                ui32AttachId,
    SYS_eMemAttrib            eMemAttrib,
    int                       buff_fd,
    PALLOC_sUmAlloc __user *  psUmAlloc
)
{
    IMG_HANDLE               hDevHandle;
    IMG_UINT32               ui32Result;
    PALLOC_sKmAlloc *        psKmAlloc;
    IMG_HANDLE               hAttachHandle;
    PALLOC_sAttachContext *  psAttachContext;
    IMG_UINT32               ui32PageNo;
    IMG_UINT32               ui32PageIdx;
    IMG_UINT64               ui64CpuPAddr;
    PALLOC_sUmAlloc          sUmAllocCp;
    IMG_UINT64 *             paui64DevAddrs;
    SYSDEVU_sInfo *          psSysDev;
    SYS_eMemPool             eMemPool;
    IMG_PVOID                pvCpuKmAddr;

    LOG_EVENT(PALLOC, PALLOC_IMPORT, LOG_FLAG_START | LOG_FLAG_QUAL_ARG1 | LOG_FLAG_QUAL_ARG2,
              ui32AttachId, buff_fd);

    DEBUG_REPORT(REPORT_MODULE_PALLOC, "PALLOC_Import1 fd %d", buff_fd);

    if (SYSOSKM_CopyFromUser(&sUmAllocCp, psUmAlloc, sizeof sUmAllocCp) != IMG_SUCCESS)
    {
        return IMG_ERROR_FATAL;
    }

    IMG_ASSERT(sUmAllocCp.bMappingOnly);

    /* Get the attachment handle from its ID... */
    ui32Result = DMANKM_GetAttachHandleFromId(ui32AttachId, &hAttachHandle);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        return ui32Result;
    }

    /* Get access to the attachment specific data...*/
    psAttachContext = DMANKM_GetCompAttachmentData(hAttachHandle);

    /* Get access to the device handle...*/
    hDevHandle = DMANKM_GetDevHandleFromAttach(hAttachHandle);

    /* Lock the device...*/
    DMANKM_LockDeviceContext(hDevHandle);

    psSysDev = SYSDEVU_GetDeviceById(SYSDEVKM_GetDeviceID(psAttachContext->hSysDevHandle));
    IMG_ASSERT(psSysDev != IMG_NULL); // I
    if (psSysDev == IMG_NULL)
    {
        ui32Result = IMG_ERROR_DEVICE_NOT_FOUND;
        goto error_get_dev_by_id;
    }

    eMemPool = (eMemAttrib & SYS_MEMATTRIB_SECURE) ? psSysDev->secureMemPool : psSysDev->sMemPool;

    /* Allocate allocation info...*/
    psKmAlloc = IMG_MALLOC(sizeof *psKmAlloc);
    IMG_ASSERT(psKmAlloc != IMG_NULL);
    if (psKmAlloc == IMG_NULL)
    {
        ui32Result = IMG_ERROR_OUT_OF_MEMORY;
        goto error_alloc_info;
    }
    IMG_MEMSET(psKmAlloc, 0, sizeof *psKmAlloc);

    /* Save device handle etc... */
    psKmAlloc->hDevHandle = hDevHandle;
    psKmAlloc->sAllocInfo.ui32Size = sUmAllocCp.ui32Size;
    psKmAlloc->sAllocInfo.bIsContiguous = IMG_FALSE;

    /* Get the device id...*/
    ui32Result = DMANKM_GetDeviceId(hDevHandle, &sUmAllocCp.ui32DeviceId);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_get_dev_id;
    }

    psKmAlloc->sAllocInfo.bMappingOnly = IMG_TRUE;

    /* Calculate the size of the allocation in pages */
    ui32PageNo = (sUmAllocCp.ui32Size + SYS_MMU_PAGE_SIZE - 1)/SYS_MMU_PAGE_SIZE;

    psKmAlloc->sAllocInfo.psSysPAddr = IMG_BIGORSMALL_ALLOC(sizeof(IMG_SYS_PHYADDR) * ui32PageNo);
    IMG_ASSERT(psKmAlloc->sAllocInfo.psSysPAddr);
    if (IMG_NULL == psKmAlloc->sAllocInfo.psSysPAddr)
    {
        ui32Result = IMG_ERROR_OUT_OF_MEMORY;
        goto error_page_array;
    }
    paui64DevAddrs = IMG_BIGORSMALL_ALLOC((sizeof *paui64DevAddrs) * ui32PageNo);
    IMG_ASSERT(paui64DevAddrs);
    if (IMG_NULL == paui64DevAddrs)
    {
        ui32Result = IMG_ERROR_OUT_OF_MEMORY;
        goto error_addr_array;
    }

    if(buff_fd >= 0)
    {
        pvCpuKmAddr = NULL;
        /* ION buffer */
#if defined ANDROID_ION_BUFFERS
        psKmAlloc->eBufType = PALLOC_BUFTYPE_ANDROIDNATIVE;
    #if defined CONFIG_X86
        ui32Result = palloc_GetIONPages(eMemPool, buff_fd, sUmAllocCp.ui32Size,
                                        psKmAlloc->sAllocInfo.psSysPAddr,
                                        &pvCpuKmAddr,
                                        &psKmAlloc->hBufHandle);
    #else // if CONFIG_X86
        ui32Result = palloc_GetIONPages(eMemPool, buff_fd, sUmAllocCp.ui32Size,
                                        psKmAlloc->sAllocInfo.psSysPAddr,
                                        NULL,
                                        &psKmAlloc->hBufHandle);
    #endif // if CONFIG_X86
        if (ui32Result != IMG_SUCCESS)
        {
            IMG_ASSERT(!"palloc_GetIONPages");
            goto error_get_pages;
        }
#else   // if ANDROID_ION_BUFFERS
        IMG_ASSERT(!"NOT ANDROID: ION not supported");
        goto error_get_pages;
#endif  // if ANDROID_ION_BUFFERS
    }
    else
    {
        /* User space allocated buffer */
        IMG_VOID __user *  pvUmBuff = ( IMG_VOID __user * ) sUmAllocCp.pvCpuUmAddr;
        IMG_ASSERT(pvUmBuff);
        psKmAlloc->hBufHandle = (IMG_HANDLE)(sUmAllocCp.pvCpuUmAddr);
        psKmAlloc->eBufType = PALLOC_BUFTYPE_USERALLOC;
        /* Assign and lock physical addresses to the user space buffer.
           The mapping of the first page in the kernel is also returned */
        ui32Result = SYSOSKM_CpuUmAddrToCpuPAddrArray(pvUmBuff, psKmAlloc->sAllocInfo.psSysPAddr,
                                                      ui32PageNo, &pvCpuKmAddr);
        IMG_ASSERT(ui32Result == IMG_SUCCESS);
        if (ui32Result != IMG_SUCCESS)
        {
            goto error_get_pages;
        }
    }

    /* Import pages */
    ui32Result = SYSMEMU_ImportExternalPages(eMemPool, sUmAllocCp.ui32Size, eMemAttrib,
                                             &psKmAlloc->hPagesHandle, pvCpuKmAddr,
                                             psKmAlloc->sAllocInfo.psSysPAddr);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_import_pages;
    }

    // Access from user space is not needed for the moment. Can be changed.
    sUmAllocCp.lOffset = 0;
#if PALLOC_EXPOSE_KM_HANDLE
    sUmAllocCp.hKmAllocHandle = psKmAlloc->hPagesHandle;
#endif /* PALLOC_EXPOSE_KM_HANDLE */

    for (ui32PageIdx = 0; ui32PageIdx < ui32PageNo; ++ui32PageIdx)
    {
        ui64CpuPAddr = psKmAlloc->sAllocInfo.psSysPAddr[ui32PageIdx];
        paui64DevAddrs[ui32PageIdx] =
            SYSDEVKM_CpuPAddrToDevPAddr(psAttachContext->hSysDevHandle, ui64CpuPAddr);
    }

    /* Register this with the resource manager */
    ui32Result = RMAN_RegisterResource(psAttachContext->hResBHandle, PALLOC_RES_TYPE_1,
                                       palloc_fnFree, psKmAlloc, IMG_NULL, &sUmAllocCp.ui32AllocId);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_resource_register;
    }

    LOG_EVENT(PALLOC, PALLOC_IMPORTID, LOG_FLAG_END | LOG_FLAG_QUAL_ARG1 | LOG_FLAG_QUAL_ARG2,
              ui32AttachId, sUmAllocCp.ui32AllocId);

    /* Unlock the device...*/
    DMANKM_UnlockDeviceContext(hDevHandle);

    /* Copy to user changed PALLOC_sUmAlloc, including physical device addresses */
    if (SYSOSKM_CopyToUser(psUmAlloc, &sUmAllocCp, sizeof sUmAllocCp))
    {
        ui32Result = IMG_ERROR_FATAL;
        goto error_copy_to_user;
    }
    if (SYSOSKM_CopyToUser(psUmAlloc->aui64DevPAddr, paui64DevAddrs,
                           (sizeof *paui64DevAddrs) * ui32PageNo))
    {
        ui32Result = IMG_ERROR_FATAL;
        goto error_copy_to_user;
    }
    /* Free the address array */
    IMG_BIGORSMALL_FREE((sizeof *paui64DevAddrs) * ui32PageNo, paui64DevAddrs);

    LOG_EVENT(PALLOC, PALLOC_IMPORT, LOG_FLAG_END | LOG_FLAG_QUAL_ARG1 | LOG_FLAG_QUAL_ARG2,
              ui32AttachId, buff_fd);

    /* Return. */
    return IMG_SUCCESS;

    /* Error handling. */
error_copy_to_user:
    /* Free everything. */
    PALLOC_Free1(sUmAllocCp.ui32AllocId);
    goto error_return;

error_resource_register:
    SYSMEMU_FreePages(psKmAlloc->hPagesHandle);
error_import_pages:
    if (buff_fd >= 0)
    {
#ifdef ANDROID_ION_BUFFERS
        palloc_ReleaseIONBuf(psKmAlloc->hBufHandle, NULL);
#endif /* ANDROID_ION_BUFFERS */
    }
    else
    {
        SYSOSKM_ReleaseCpuPAddrArray(pvCpuKmAddr, psKmAlloc->hBufHandle,
                                     psKmAlloc->sAllocInfo.psSysPAddr, ui32PageNo);
    }
error_get_pages:
    IMG_BIGORSMALL_FREE((sizeof *paui64DevAddrs) * ui32PageNo, paui64DevAddrs);
error_addr_array:
    IMG_BIGORSMALL_FREE(sizeof(IMG_SYS_PHYADDR) * ui32PageNo, psKmAlloc->sAllocInfo.psSysPAddr);
error_page_array:
error_get_dev_id:
    IMG_FREE(psKmAlloc);
error_alloc_info:
error_get_dev_by_id:
    /* Unlock the device. */
    DMANKM_UnlockDeviceContext(hDevHandle);

error_return:
    return ui32Result;
}
/*!
******************************************************************************

 @Function                PALLOC_Alloc1

******************************************************************************/
IMG_RESULT PALLOC_Alloc1(
    IMG_UINT32                ui32AttachId,
    SYS_eMemAttrib            eMemAttrib,
    PALLOC_sUmAlloc __user *  psUmAlloc
)
{
    IMG_HANDLE               hDevHandle;
    IMG_UINT32               ui32Result;
    PALLOC_sKmAlloc *        psKmAlloc;
    IMG_HANDLE               hAttachHandle;
    PALLOC_sAttachContext *  psAttachContext;
    IMG_UINT32               ui32PageNo;
    PALLOC_sUmAlloc          sUmAllocCp;
    IMG_UINT32               ui32PageIdx;
    IMG_UINT64 *             pui64Phys;
    SYSMEMU_sPages *         psSysMem;
    SYS_eMemPool             eMemPool;
    SYSDEVU_sInfo *          psSysDev;
    /* the following code assumes that IMG_SYS_PHYADDR and IMG_UINT64 are the same size */

#ifndef SYSBRG_BRIDGING
    IMG_VOID *               pvKmAddr;
#endif

    if (SYSOSKM_CopyFromUser(&sUmAllocCp, psUmAlloc, sizeof(sUmAllocCp)) != IMG_SUCCESS)
    {
        return IMG_ERROR_FATAL;
    }

    LOG_EVENT(PALLOC, PALLOC_ALLOC, (LOG_FLAG_START | LOG_FLAG_QUAL_ARG1 |LOG_FLAG_QUAL_ARG2), ui32AttachId, sUmAllocCp.ui32Size);

    IMG_ASSERT(!sUmAllocCp.bMappingOnly);

    /* Get the attachment handle from its ID...*/
    ui32Result = DMANKM_GetAttachHandleFromId(ui32AttachId, &hAttachHandle);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        return ui32Result;
    }

    /* Get access to the attachment specific data...*/
    psAttachContext = DMANKM_GetCompAttachmentData(hAttachHandle);

    /* Get access to the device handle...*/
    hDevHandle = DMANKM_GetDevHandleFromAttach(hAttachHandle);

    /* Lock the device...*/
    DMANKM_LockDeviceContext(hDevHandle);

    psSysDev = SYSDEVU_GetDeviceById(SYSDEVKM_GetDeviceID(psAttachContext->hSysDevHandle));
    IMG_ASSERT(psSysDev != IMG_NULL); // I
    if (psSysDev == IMG_NULL)
    {
        return IMG_ERROR_DEVICE_NOT_FOUND;
    }

    eMemPool = (eMemAttrib & SYS_MEMATTRIB_SECURE) ? psSysDev->secureMemPool : psSysDev->sMemPool;

    /* Allocate allocation info...*/
    psKmAlloc = IMG_MALLOC(sizeof(*psKmAlloc));
    IMG_ASSERT(psKmAlloc != IMG_NULL);
    if (psKmAlloc == IMG_NULL)
    {
        ui32Result = IMG_ERROR_OUT_OF_MEMORY;
        goto error_alloc_info;
    }
    IMG_MEMSET(psKmAlloc, 0, sizeof(*psKmAlloc));

    /* Save device handle etc...*/
    psKmAlloc->hDevHandle          = hDevHandle;
    psKmAlloc->sAllocInfo.ui32Size = sUmAllocCp.ui32Size;
    psKmAlloc->hBufHandle          = NULL;
    psKmAlloc->eBufType            = PALLOC_BUFTYPE_PALLOCATED;

    /* Allocate pages...*/
    ui32Result = SYSMEMU_AllocatePages(sUmAllocCp.ui32Size, eMemAttrib, eMemPool,
                                       &psKmAlloc->hPagesHandle, &pui64Phys);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_alloc_pages;
    }

#ifndef SYSBRG_BRIDGING
    SYSMEMU_GetCpuKmAddr(&pvKmAddr, psKmAlloc->hPagesHandle);
    IMG_ASSERT(pvKmAddr != IMG_NULL);
    if(pvKmAddr == IMG_NULL)
    {
        ui32Result = IMG_ERROR_FATAL;
        goto error_cpu_km_addr;
    }
#endif

    /* Return addresses...*/
    psSysMem = psKmAlloc->hPagesHandle;


#ifdef PALLOC_EXPOSE_KM_HANDLE
    sUmAllocCp.hKmAllocHandle = psKmAlloc->hPagesHandle;
#endif

    /* Check if contiguous...*/
    psKmAlloc->sAllocInfo.bIsContiguous = SYSMEMKM_IsContiguous(psKmAlloc->hPagesHandle);

    /* Get the device id...*/
    ui32Result = DMANKM_GetDeviceId(hDevHandle, &sUmAllocCp.ui32DeviceId);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_get_dev_id;
    }

    sUmAllocCp.lOffset = 0;
    if (psSysMem->hRegHandle)
    {
        // Determine the offset to memory if it has been made mappable in UM.
        sUmAllocCp.lOffset = (long)pui64Phys[0];
    }

    /* Calculate the size of the allocation in pages...*/
    ui32PageNo = (sUmAllocCp.ui32Size + SYS_MMU_PAGE_SIZE - 1)/SYS_MMU_PAGE_SIZE;
    psKmAlloc->sAllocInfo.psSysPAddr = IMG_BIGORSMALL_ALLOC(sizeof(IMG_SYS_PHYADDR) * ui32PageNo);
    IMG_ASSERT(psKmAlloc->sAllocInfo.psSysPAddr);
    if (IMG_NULL == psKmAlloc->sAllocInfo.psSysPAddr)
    {
        ui32Result = IMG_ERROR_OUT_OF_MEMORY;
        goto error_page_array;
    }
    IMG_MEMSET(psKmAlloc->sAllocInfo.psSysPAddr, 0, sizeof(IMG_SYS_PHYADDR) * ui32PageNo);

    for (ui32PageIdx = 0; ui32PageIdx < ui32PageNo; ++ui32PageIdx)
    {
        psKmAlloc->sAllocInfo.psSysPAddr[ui32PageIdx] = pui64Phys[ui32PageIdx];
    }

    /* Register this with the resource manager...*/
    ui32Result = RMAN_RegisterResource(psAttachContext->hResBHandle, PALLOC_RES_TYPE_1,
                                       palloc_fnFree, psKmAlloc, IMG_NULL, &sUmAllocCp.ui32AllocId);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_resource_register;
    }

    LOG_EVENT(PALLOC, PALLOC_ALLOCID, (LOG_FLAG_END | LOG_FLAG_QUAL_ARG1 |LOG_FLAG_QUAL_ARG2),
              ui32AttachId, sUmAllocCp.ui32AllocId);

    /* Unlock the device...*/
    DMANKM_UnlockDeviceContext(hDevHandle);

    /* Copy to user changed PALLOC_sUmAlloc, including physical device addresses */
    if (SYSOSKM_CopyToUser(psUmAlloc, &sUmAllocCp, sizeof(sUmAllocCp)))
    {
        ui32Result = IMG_ERROR_FATAL;
        goto error_copy_to_user;
    }
    if (SYSOSKM_CopyToUser(psUmAlloc->aui64DevPAddr, psKmAlloc->sAllocInfo.psSysPAddr,
                           sizeof(psKmAlloc->sAllocInfo.psSysPAddr[0]) * ui32PageNo))
    {
        ui32Result = IMG_ERROR_FATAL;
        goto error_copy_to_user;
    }

    LOG_EVENT(PALLOC, PALLOC_ALLOC, (LOG_FLAG_END | LOG_FLAG_QUAL_ARG1 |LOG_FLAG_QUAL_ARG2),
              ui32AttachId, sUmAllocCp.ui32Size);

    /* Return. */
    return IMG_SUCCESS;

    /* Error handling. */
error_copy_to_user:
    /* Free everything. */
    PALLOC_Free1(sUmAllocCp.ui32AllocId);
    goto error_return;

error_resource_register:
    IMG_BIGORSMALL_FREE(sizeof(IMG_SYS_PHYADDR) * ui32PageNo,
                        psKmAlloc->sAllocInfo.psSysPAddr);
error_page_array:
error_get_dev_id:
#ifndef SYSBRG_BRIDGING
error_cpu_km_addr:
#endif /* SYSBRG_BRIDGING */
    SYSMEMU_FreePages(psKmAlloc->hPagesHandle);
error_alloc_pages:
    IMG_FREE(psKmAlloc);
error_alloc_info:
    /* Unlock the device. */
    DMANKM_UnlockDeviceContext(hDevHandle);

error_return:
    return ui32Result;
}