/*
 *  ion.c
 *
 * Memory Allocator functions for ion
 *
 *   Copyright 2011 Google, Inc
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
#include <config.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
     
#include <directfb.h>
#include <core/core.h>
#include <misc/conf.h>

#include "libion.h"

int ion_open()
{
        int fd = open("/dev/ion", O_RDWR);
        if (fd < 0)
                printf("open /dev/ion failed!\n");
        return fd;
}

int ion_close(int fd)
{
        return close(fd);
}

static int ion_ioctl(int fd, int req, void *arg)
{
        int ret = ioctl(fd, req, arg);
        if (ret < 0) {
                printf("ioctl %x failed with code %d: %s\n", req,
                       ret, strerror(errno));
                return -errno;
        }
        return ret;
}

int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
	      unsigned int flags, ion_handle_t *handle)
{
        int ret;
        struct ion_allocation_data data = {
                .len = len,
                .align = align,
#if KERNEL_3_10_23
		        .heap_id_mask = heap_mask,
                .flags = flags,
#elif KERNEL_3_1_10
                .flags = heap_mask,
#endif
        };

        ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
        if (ret < 0)
                return ret;
        *handle = data.handle;
        return ret;
}

int ion_free(int fd, ion_handle_t handle)
{
        struct ion_handle_data data = {
                .handle = handle,
        };
        return ion_ioctl(fd, ION_IOC_FREE, &data);
}

int ion_share(int fd, ion_handle_t handle, int *shared_fd)
{
        int map_fd;
        struct ion_fd_data data = {
                .handle = handle,
        };

        int ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
        if (ret < 0)
                return ret;
        *shared_fd = data.fd;
        if (*shared_fd < 0) {
                printf("share ioctl returned negative fd\n");
                return -1;
        }
        return ret;
}

int ion_import(int fd, int shared_fd, ion_handle_t *handle)
{
        struct ion_fd_data data = {
                .fd = shared_fd,
        };

        int ret = ion_ioctl(fd, ION_IOC_IMPORT, &data);
        if (ret < 0)
                return ret;
        *handle = data.handle;
        return ret;
}

/*ion cust commands */

//lock phy/tlb addr
int ion_lock(int fd, ion_lock_data *lock_data)
{
    ion_cust_alloc_data data = {
#if KERNEL_3_10_23
        .handle_id = lock_data->handle,
#elif KERNEL_3_1_10
        .handle = lock_data->handle,
#endif
       };
	struct ion_custom_data custom_data;

    custom_data.cmd = MTLB_CUST_IOC_LOCK;
	custom_data.arg = &data;

    int ret = ion_ioctl(fd, ION_IOC_CUSTOM, &custom_data);
    if (ret < 0)
    {
        return ret;
    }
    lock_data->addressType = data.addressType;
    lock_data->mapaddr = data.mapaddr;
    lock_data->miu = data.miu;
    lock_data->miu_offset = data.miu_offset;
    lock_data->tlb_table_change = data.tlb_table_change;
    return ret;
}

//lock phy/tlb addr
int ion_unlock(int fd, ion_lock_data *lock_data)
{
    ion_cust_alloc_data data;
    memcpy(&data, lock_data, sizeof(ion_cust_alloc_data));

	struct ion_custom_data custom_data;

    custom_data.cmd = MTLB_CUST_IOC_UNLOCK;
	custom_data.arg = &data;

    int ret = ion_ioctl(fd, ION_IOC_CUSTOM, &custom_data);
    return ret;
}

//map share_fd to different process
int ion_cust_import_fd(int fd, int pid, int shared_fd, int *newfd)
{
        ion_cust_import data;
        data.pid = pid;
        data.fd = shared_fd;

        struct ion_custom_data custom_data;
        
        custom_data.cmd = MTLB_CUST_IOC_IMPORT;
        custom_data.arg = &data;
        
        int ret = ion_ioctl(fd, ION_IOC_CUSTOM, &custom_data);
        if (ret < 0)
                return ret;
        *newfd = data.newfd;
        return ret;
}

//get the hw info, check weather support tlb , whichi client support tlb, GE/GOP/MIU?
int ion_cust_get_tlbinfo(int fd, mtlb_hardware_info *hw_info)
{
	struct ion_custom_data custom_data;
    mtlb_hardware_info hw_data;

    custom_data.cmd = MTLB_CUST_IOC_GET_HWINO;
	custom_data.arg = &hw_data;

    int ret = ion_ioctl(fd, ION_IOC_CUSTOM, &custom_data);
    if(ret < 0)
    {
        return ret;
    }
    memcpy(hw_info, &hw_data, sizeof(mtlb_hardware_info));
    return ret;
}


int ion_cust_tlb_init(int fd)
{
	struct ion_custom_data custom_data;

    custom_data.cmd = MTLB_CUST_IOC_INIT;
	custom_data.arg = 0;
	
    return ion_ioctl(fd, ION_IOC_CUSTOM, &custom_data);
}

int ion_cust_tlb_enable(int fd, mtlb_tlbclient_enable *enable_data)
{
	struct ion_custom_data custom_data;

    custom_data.cmd = MTLB_CUST_IOC_ENABLE;
	custom_data.arg = enable_data;

    return ion_ioctl(fd, ION_IOC_CUSTOM, &custom_data);
}

