M7350/qcom-opensource/kernel/kernel-tests/ion/msm_ion_test_module.c
2024-09-09 08:57:42 +00:00

440 lines
9.6 KiB
C

/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but 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.
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/msm_ion.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/version.h>
#include "iontest.h"
#include "compat_msm_ion_test_module.h"
#define ION_TEST_DEV_NAME "msm_ion_test"
#define CLIENT_NAME "ion_test_client"
struct msm_ion_test {
struct ion_client *ion_client;
struct ion_handle *ion_handle;
struct ion_test_data test_data;
};
struct heap {
unsigned int id;
unsigned int size;
enum ion_test_heap_type type;
};
static struct class *ion_test_class;
static int ion_test_major;
static struct device *ion_test_dev;
static struct heap *heap_list;
static unsigned int heap_list_len;
/*Utility apis*/
static inline int create_ion_client(struct msm_ion_test *ion_test)
{
ion_test->ion_client = msm_ion_client_create(CLIENT_NAME);
if (IS_ERR_OR_NULL(ion_test->ion_client))
return -EIO;
return 0;
}
static inline void free_ion_client(struct msm_ion_test *ion_test)
{
ion_client_destroy(ion_test->ion_client);
}
static int alloc_ion_buf(struct msm_ion_test *ion_test,
struct ion_test_data *test_data)
{
ion_test->ion_handle = ion_alloc(ion_test->ion_client, test_data->size,
test_data->align,
test_data->heap_id_mask,
test_data->flags);
if (IS_ERR_OR_NULL(ion_test->ion_handle))
return -EIO;
return 0;
}
static inline void free_ion_buf(struct msm_ion_test *ion_test)
{
ion_free(ion_test->ion_client, ion_test->ion_handle);
}
static int heap_detected;
#ifdef CONFIG_OF_DEVICE
static int ion_find_heaps_available(void)
{
struct device_node *ion_node;
struct device_node *ion_child;
unsigned int i = 0;
int ret = 0;
unsigned int val = 0;
heap_list_len = 0;
ion_node = of_find_compatible_node(NULL, NULL, "qcom,msm-ion");
for_each_child_of_node(ion_node, ion_child)
heap_list_len++;
heap_list = kzalloc(sizeof(struct heap) * heap_list_len, GFP_KERNEL);
if (!heap_list) {
ret = -ENOMEM;
goto out;
}
for_each_child_of_node(ion_node, ion_child) {
ret = of_property_read_u32(ion_child, "reg", &val);
if (ret) {
pr_err("%s: Unable to find reg key", __func__);
goto free_heaps;
}
heap_list[i].id = val;
switch (val) {
case ION_SYSTEM_HEAP_ID:
{
heap_list[i].type = SYSTEM_MEM;
break;
}
case ION_SYSTEM_CONTIG_HEAP_ID:
{
heap_list[i].type = SYSTEM_CONTIG;
break;
}
case ION_CP_MM_HEAP_ID:
{
ret = of_property_read_u32(ion_child,
"qcom,memory-reservation-size", &val);
if (ret) {
heap_list[i].type = SECURE_DMA;
ret = 0;
} else {
heap_list[i].size = val;
heap_list[i].type = CP_CARVEOUT;
}
break;
}
case ION_QSECOM_HEAP_ID:
case ION_AUDIO_HEAP_ID:
{
ret = of_property_read_u32(ion_child,
"qcom,memory-reservation-size", &val);
if (ret) {
heap_list[i].type = DMA;
ret = 0;
} else {
heap_list[i].size = val;
heap_list[i].type = CARVEOUT;
}
break;
}
default:
break;
}
++i;
}
heap_detected = 1;
goto out;
free_heaps:
kfree(heap_list);
heap_list = 0;
out:
return ret;
}
#else
static int ion_find_heaps_available(void)
{
return 0;
}
#endif
static int ion_test_open(struct inode *inode, struct file *file)
{
struct msm_ion_test *ion_test = kzalloc(sizeof(struct msm_ion_test),
GFP_KERNEL);
if (!ion_test)
return -ENOMEM;
pr_debug("ion test device opened\n");
file->private_data = ion_test;
return ion_find_heaps_available();
}
static int get_proper_heap(struct ion_heap_data *data)
{
int ret = -EINVAL;
unsigned int i;
if (!heap_detected) {
data->valid = 1;
ret = 0;
} else {
data->heap_id_mask = 0;
data->size = 0;
data->valid = 0;
for (i = 0; i < heap_list_len; ++i) {
if (data->type == heap_list[i].type) {
data->size = heap_list[i].size;
data->heap_id_mask = ION_HEAP(heap_list[i].id);
data->valid = 1;
ret = 0;
break;
}
}
}
return ret;
}
static long ion_test_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
int ret;
ion_phys_addr_t phys_addr;
void *addr;
size_t len;
unsigned long flags;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0))
unsigned long size;
#else
size_t size;
#endif
struct msm_ion_test *ion_test = file->private_data;
struct ion_test_data *test_data = &ion_test->test_data;
switch (cmd) {
case IOC_ION_KCLIENT_CREATE:
{
ret = create_ion_client(ion_test);
break;
}
case IOC_ION_KCLIENT_DESTROY:
{
free_ion_client(ion_test);
ret = 0;
break;
}
case IOC_ION_KALLOC:
{
if (copy_from_user(test_data, (void __user *)arg,
sizeof(struct ion_test_data)))
return -EFAULT;
ret = alloc_ion_buf(ion_test, test_data);
if (ret)
pr_info("allocating ion buffer failed\n");
break;
}
case IOC_ION_KFREE:
{
free_ion_buf(ion_test);
ret = 0;
break;
}
case IOC_ION_KPHYS:
{
ret = ion_phys(ion_test->ion_client, ion_test->ion_handle,
&phys_addr, &len);
if (!ret)
pr_info("size is 0x%zx\n phys addr 0x%x", len,
(unsigned int)phys_addr);
break;
}
case IOC_ION_KMAP:
{
addr = ion_map_kernel(ion_test->ion_client,
ion_test->ion_handle);
if (IS_ERR_OR_NULL(addr)) {
ret = -EIO;
pr_info("mapping kernel buffer failed\n");
} else {
ret = 0;
test_data->vaddr = (unsigned long)addr;
}
break;
}
case IOC_ION_KUMAP:
{
ion_unmap_kernel(ion_test->ion_client, ion_test->ion_handle);
ret = 0;
break;
}
case IOC_ION_UIMPORT:
{
if (copy_from_user(test_data, (void __user *)arg,
sizeof(struct ion_test_data)))
return -EFAULT;
ion_test->ion_handle = ion_import_dma_buf(ion_test->ion_client,
test_data->shared_fd);
if (IS_ERR_OR_NULL(ion_test->ion_handle)) {
ret = -EIO;
pr_info("import of user buf failed\n");
} else
ret = 0;
break;
}
case IOC_ION_UBUF_FLAGS:
{
ret = ion_handle_get_flags(ion_test->ion_client,
ion_test->ion_handle, &flags);
if (ret)
pr_info("user flags cannot be retrieved\n");
else
if (copy_to_user((void __user *)arg, &flags,
sizeof(unsigned long)))
ret = -EFAULT;
break;
}
case IOC_ION_UBUF_SIZE:
{
ret = ion_handle_get_size(ion_test->ion_client,
ion_test->ion_handle, &size);
if (ret)
pr_info("buffer size cannot be retrieved\n");
else
if (copy_to_user((void __user *)arg, &size,
sizeof(unsigned long)))
ret = -EFAULT;
break;
}
case IOC_ION_WRITE_VERIFY:
{
write_pattern(test_data->vaddr, test_data->size);
if (verify_pattern(test_data->vaddr, test_data->size)) {
pr_info("verify of mapped buf failed\n");
ret = -EIO;
} else
ret = 0;
break;
}
case IOC_ION_VERIFY:
{
if (verify_pattern(test_data->vaddr, test_data->size)) {
pr_info("fail in verifying imported buffer\n");
ret = -EIO;
} else
ret = 0;
break;
}
case IOC_ION_FIND_PROPER_HEAP:
{
struct ion_heap_data data;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_heap_data)))
return -EFAULT;
ret = get_proper_heap(&data);
if (!ret) {
if (copy_to_user((void __user *)arg, &data,
sizeof(struct ion_heap_data)))
ret = -EFAULT;
}
break;
}
default:
{
pr_info("command not supported\n");
ret = -EINVAL;
}
};
return ret;
}
static int ion_test_release(struct inode *inode, struct file *file)
{
struct msm_ion_test *ion_test = file->private_data;
pr_debug("ion test device closed\n");
kfree(ion_test);
return 0;
}
/*
* Register ourselves as a device to be able to test the ion code
* from userspace.
*/
static const struct file_operations ion_test_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ion_test_ioctl,
.compat_ioctl = compat_ion_test_ioctl,
.open = ion_test_open,
.release = ion_test_release,
};
static int ion_test_device_create(void)
{
int ret_val = 0;
ion_test_major = register_chrdev(0, ION_TEST_DEV_NAME, &ion_test_fops);
if (ion_test_major < 0) {
pr_err("Unable to register chrdev: %d\n", ion_test_major);
ret_val = ion_test_major;
goto out;
}
ion_test_class = class_create(THIS_MODULE, ION_TEST_DEV_NAME);
if (IS_ERR(ion_test_class)) {
ret_val = PTR_ERR(ion_test_class);
pr_err("Unable to create class: %d\n", ret_val);
goto err_create_class;
}
ion_test_dev = device_create(ion_test_class, NULL, MKDEV(ion_test_major, 0), NULL, ION_TEST_DEV_NAME);
if (IS_ERR(ion_test_dev)) {
ret_val = PTR_ERR(ion_test_dev);
pr_err("Unable to create device: %d\n", ret_val);
goto err_create_device;
}
goto out;
err_create_device:
class_destroy(ion_test_class);
err_create_class:
unregister_chrdev(ion_test_major, ION_TEST_DEV_NAME);
out:
return ret_val;
}
static void ion_test_device_destroy(void)
{
device_destroy(ion_test_class, MKDEV(ion_test_major, 0));
class_destroy(ion_test_class);
unregister_chrdev(ion_test_major, ION_TEST_DEV_NAME);
kfree(heap_list);
}
static int ion_test_init(void)
{
return ion_test_device_create();
}
static void ion_test_exit(void)
{
return ion_test_device_destroy();
}
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Test for MSM ION implementation");
module_init(ion_test_init);
module_exit(ion_test_exit);