From 2078454a6f648e4fd35c8bfa23695fd4f89748f8 Mon Sep 17 00:00:00 2001 From: Show Liu Date: Tue, 20 May 2014 15:56:19 +0800 Subject: add dma_buf test exporter --- drivers/base/dma_buf_test_exporter/Kbuild | 18 + drivers/base/dma_buf_test_exporter/Kconfig | 20 + drivers/base/dma_buf_test_exporter/Makefile | 30 + .../dma_buf_test_exporter/dma-buf-test-exporter.c | 644 +++++++++++++++++++++ drivers/base/dma_buf_test_exporter/sconscript | 36 ++ include/linux/dma-buf-test-exporter.h | 78 +++ 6 files changed, 826 insertions(+) create mode 100755 drivers/base/dma_buf_test_exporter/Kbuild create mode 100755 drivers/base/dma_buf_test_exporter/Kconfig create mode 100755 drivers/base/dma_buf_test_exporter/Makefile create mode 100755 drivers/base/dma_buf_test_exporter/dma-buf-test-exporter.c create mode 100755 drivers/base/dma_buf_test_exporter/sconscript create mode 100755 include/linux/dma-buf-test-exporter.h diff --git a/drivers/base/dma_buf_test_exporter/Kbuild b/drivers/base/dma_buf_test_exporter/Kbuild new file mode 100755 index 00000000000..56b9f86d2d5 --- /dev/null +++ b/drivers/base/dma_buf_test_exporter/Kbuild @@ -0,0 +1,18 @@ +# +# (C) COPYRIGHT 2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +ifneq ($(CONFIG_DMA_SHARED_BUFFER),) +obj-$(CONFIG_DMA_SHARED_BUFFER_TEST_EXPORTER) += dma-buf-test-exporter.o +endif diff --git a/drivers/base/dma_buf_test_exporter/Kconfig b/drivers/base/dma_buf_test_exporter/Kconfig new file mode 100755 index 00000000000..974f0c23dbd --- /dev/null +++ b/drivers/base/dma_buf_test_exporter/Kconfig @@ -0,0 +1,20 @@ +# +# (C) COPYRIGHT 2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +config DMA_SHARED_BUFFER_TEST_EXPORTER + tristate "Test exporter for the dma-buf framework" + depends on DMA_SHARED_BUFFER + help + This option enables the test exporter usable to help test importerts. diff --git a/drivers/base/dma_buf_test_exporter/Makefile b/drivers/base/dma_buf_test_exporter/Makefile new file mode 100755 index 00000000000..06e3d5c121a --- /dev/null +++ b/drivers/base/dma_buf_test_exporter/Makefile @@ -0,0 +1,30 @@ +# +# (C) COPYRIGHT 2011-2013 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +# linux build system bootstrap for out-of-tree module + +# default to building for the host +ARCH ?= $(shell uname -m) + +ifeq ($(KDIR),) +$(error Must specify KDIR to point to the kernel to target)) +endif + +all: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../include" CONFIG_DMA_SHARED_BUFFER_TEST_EXPORTER=m + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean + diff --git a/drivers/base/dma_buf_test_exporter/dma-buf-test-exporter.c b/drivers/base/dma_buf_test_exporter/dma-buf-test-exporter.c new file mode 100755 index 00000000000..818479b562a --- /dev/null +++ b/drivers/base/dma_buf_test_exporter/dma-buf-test-exporter.c @@ -0,0 +1,644 @@ +/* + * + * (C) COPYRIGHT ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) +#include +#include +#endif +#include + +struct dma_buf_te_alloc +{ + /* the real alloc */ + int nr_pages; + struct page **pages; + + /* the debug usage tracking */ + int nr_attached_devices; + int nr_device_mappings; + int nr_cpu_mappings; + + /* failure simulation */ + int fail_attach; + int fail_map; + int fail_mmap; + + bool contiguous; + dma_addr_t contig_dma_addr; + void *contig_cpu_addr; +}; + +static struct miscdevice te_device; + +static int dma_buf_te_attach(struct dma_buf *buf, struct device *dev, struct dma_buf_attachment *attachment) +{ + struct dma_buf_te_alloc *alloc; + alloc = buf->priv; + + if (alloc->fail_attach) + return -EFAULT; + + /* dma_buf is externally locked during call */ + alloc->nr_attached_devices++; + return 0; +} + +static void dma_buf_te_detach(struct dma_buf *buf, struct dma_buf_attachment *attachment) +{ + struct dma_buf_te_alloc *alloc; + alloc = buf->priv; + /* dma_buf is externally locked during call */ + + alloc->nr_attached_devices--; +} + +static struct sg_table *dma_buf_te_map(struct dma_buf_attachment *attachment, enum dma_data_direction direction) +{ + struct sg_table *sg; + struct scatterlist *iter; + struct dma_buf_te_alloc *alloc; + int i; + int ret; + + alloc = attachment->dmabuf->priv; + + if (alloc->fail_map) + return ERR_PTR(-ENOMEM); + +#ifndef ARCH_HAS_SG_CHAIN + /* if the ARCH can't chain we can't have allocs larger than a single sg can hold */ + if (alloc->nr_pages > SG_MAX_SINGLE_ALLOC) + return ERR_PTR(-EINVAL); +#endif + + sg = (struct sg_table*)kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!sg) + return ERR_PTR(-ENOMEM); + + /* from here we access the allocation object, so lock the dmabuf pointing to it */ + mutex_lock(&attachment->dmabuf->lock); + + if (alloc->contiguous) + ret = sg_alloc_table(sg, 1, GFP_KERNEL); + else + ret = sg_alloc_table(sg, alloc->nr_pages, GFP_KERNEL); + if (ret) { + mutex_unlock(&attachment->dmabuf->lock); + kfree(sg); + return ERR_PTR(ret); + } + + if (alloc->contiguous) { + sg_dma_len(sg->sgl) = alloc->nr_pages * PAGE_SIZE; + sg_set_page(sg->sgl, pfn_to_page(PFN_DOWN(alloc->contig_dma_addr)), + alloc->nr_pages * PAGE_SIZE, 0); + sg_dma_address(sg->sgl) = alloc->contig_dma_addr; + } else { + for_each_sg(sg->sgl, iter, alloc->nr_pages, i) + sg_set_page(iter, alloc->pages[i], PAGE_SIZE, 0); + } + + if (!dma_map_sg(attachment->dev, sg->sgl, sg->nents, direction)) { + mutex_unlock(&attachment->dmabuf->lock); + sg_free_table(sg); + kfree(sg); + return ERR_PTR(-ENOMEM); + } + + alloc->nr_device_mappings++; + mutex_unlock(&attachment->dmabuf->lock); + return sg; +} + +static void dma_buf_te_unmap(struct dma_buf_attachment *attachment, + struct sg_table *sg, enum dma_data_direction direction) +{ + struct dma_buf_te_alloc *alloc; + + alloc = attachment->dmabuf->priv; + + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, direction); + sg_free_table(sg); + kfree(sg); + +#ifdef CONFIG_ARM64 + /* TODO (MID64-78): The latest version of the Linux kernel (3.11 as of + * this writing) assumes full hardware cache coherency on ARM64 and thus + * won't explicitly flush any CPU caches as part of DMA unmap. Our test + * platform, however, is not cache coherent. We call flush_cache_all() as a + * workaround until non-coherent DMA is supported in the ARM64 kernel. + */ + flush_cache_all(); +#endif + + mutex_lock(&attachment->dmabuf->lock); + alloc->nr_device_mappings--; + mutex_unlock(&attachment->dmabuf->lock); +} + +static void dma_buf_te_release(struct dma_buf *buf) +{ + int i; + struct dma_buf_te_alloc *alloc; + alloc = buf->priv; + /* no need for locking */ + + dev_info(te_device.this_device, "%s", __func__); + + if (alloc->contiguous) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) + DEFINE_DMA_ATTRS(attrs); + + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); + dma_free_attrs(te_device.this_device, + alloc->nr_pages * PAGE_SIZE, + alloc->contig_cpu_addr, alloc->contig_dma_addr, &attrs); +#else + dma_free_writecombine(te_device.this_device, + alloc->nr_pages * PAGE_SIZE, + alloc->contig_cpu_addr, alloc->contig_dma_addr); +#endif + } else { + for (i = 0; i < alloc->nr_pages; i++) { + __free_page(alloc->pages[i]); + } + } + kfree(alloc->pages); + kfree(alloc); +} + + +static void dma_buf_te_mmap_open(struct vm_area_struct *vma) +{ + struct dma_buf *dma_buf; + struct dma_buf_te_alloc *alloc; + dma_buf = vma->vm_private_data; + alloc = dma_buf->priv; + + mutex_lock(&dma_buf->lock); + alloc->nr_cpu_mappings++; + mutex_unlock(&dma_buf->lock); +} + +static void dma_buf_te_mmap_close(struct vm_area_struct *vma) +{ + struct dma_buf *dma_buf; + struct dma_buf_te_alloc *alloc; + dma_buf = vma->vm_private_data; + alloc = dma_buf->priv; + + BUG_ON(alloc->nr_cpu_mappings <= 0); + mutex_lock(&dma_buf->lock); + alloc->nr_cpu_mappings--; + mutex_unlock(&dma_buf->lock); +} + +static int dma_buf_te_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct dma_buf_te_alloc *alloc; + struct dma_buf *dmabuf; + struct page *pageptr; + + dmabuf = vma->vm_private_data; + alloc = dmabuf->priv; + + if (vmf->pgoff > alloc->nr_pages) + return VM_FAULT_SIGBUS; + + pageptr = alloc->pages[vmf->pgoff]; + + BUG_ON(!pageptr); + + get_page(pageptr); + vmf->page = pageptr; + + return 0; +} + +struct vm_operations_struct dma_buf_te_vm_ops = +{ + .open = dma_buf_te_mmap_open, + .close = dma_buf_te_mmap_close, + .fault = dma_buf_te_mmap_fault +}; + +static int dma_buf_te_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct dma_buf_te_alloc *alloc; + alloc = dmabuf->priv; + + if (alloc->fail_mmap) + return -ENOMEM; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; +#else + vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTEXPAND; +#endif + vma->vm_ops = &dma_buf_te_vm_ops; + vma->vm_private_data = dmabuf; + vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); + + /* we fault in the pages on access */ + + /* call open to do the ref-counting */ + dma_buf_te_vm_ops.open(vma); + + return 0; +} + +static void *dma_buf_te_kmap_atomic(struct dma_buf *buf, unsigned long page_num) +{ + /* IGNORE */ + return NULL; +} + +static void *dma_buf_te_kmap(struct dma_buf *buf, unsigned long page_num) +{ + /* IGNORE */ + return NULL; +} + +static struct dma_buf_ops dma_buf_te_ops = +{ + /* real handlers */ + .attach = dma_buf_te_attach, + .detach = dma_buf_te_detach, + .map_dma_buf = dma_buf_te_map, + .unmap_dma_buf = dma_buf_te_unmap, + .release = dma_buf_te_release, + .mmap = dma_buf_te_mmap, + + /* nop handlers for mandatory functions we ignore */ + .kmap_atomic = dma_buf_te_kmap_atomic, + .kmap = dma_buf_te_kmap +}; + +static int do_dma_buf_te_ioctl_version(struct dma_buf_te_ioctl_version __user *buf) +{ + struct dma_buf_te_ioctl_version v; + + if (copy_from_user(&v, buf, sizeof(v))) + return -EFAULT; + + if (v.op != DMA_BUF_TE_ENQ) + return -EFAULT; + + v.op = DMA_BUF_TE_ACK; + v.major = DMA_BUF_TE_VER_MAJOR; + v.minor = DMA_BUF_TE_VER_MINOR; + + if (copy_to_user(buf, &v, sizeof(v))) + return -EFAULT; + else + return 0; +} + +static int do_dma_buf_te_ioctl_alloc(struct dma_buf_te_ioctl_alloc __user *buf, bool contiguous) +{ + struct dma_buf_te_ioctl_alloc alloc_req; + struct dma_buf_te_alloc *alloc; + struct dma_buf *dma_buf; + int i = 0; + int fd; + + if (copy_from_user(&alloc_req, buf, sizeof(alloc_req))) { + dev_err(te_device.this_device, "%s: couldn't get user data", + __func__); + goto no_input; + } + + if (!alloc_req.size) { + dev_err(te_device.this_device,"%s: no size specified", + __func__); + goto zero_size; + } + + alloc = (struct dma_buf_te_alloc*)kzalloc(sizeof(struct dma_buf_te_alloc), GFP_KERNEL); + if (NULL == alloc) { + dev_err(te_device.this_device, "%s: couldn't alloc object", + __func__); + goto no_alloc_object; + } + + alloc->nr_pages = alloc_req.size; + alloc->contiguous = contiguous; + + alloc->pages = (struct page**)kzalloc(sizeof(struct page*) * alloc->nr_pages, GFP_KERNEL); + if (!alloc->pages) { + dev_err(te_device.this_device, + "%s: couldn't alloc %d page structures", __func__, + alloc->nr_pages); + goto free_alloc_object; + } + + if (contiguous) { + dma_addr_t dma_aux; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) + DEFINE_DMA_ATTRS(attrs); + + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); + alloc->contig_cpu_addr = dma_alloc_attrs(te_device.this_device, + alloc->nr_pages * PAGE_SIZE, + &alloc->contig_dma_addr, GFP_KERNEL, &attrs); +#else + alloc->contig_cpu_addr = dma_alloc_writecombine(te_device.this_device, + alloc->nr_pages * PAGE_SIZE, + &alloc->contig_dma_addr, GFP_KERNEL); +#endif + if (!alloc->contig_cpu_addr) { + dev_err(te_device.this_device, + "%s: couldn't alloc contiguous buffer %d pages", + __func__, alloc->nr_pages); + goto free_page_struct; + } + dma_aux = alloc->contig_dma_addr; + for (i = 0; i < alloc->nr_pages; i++) { + alloc->pages[i] = pfn_to_page(PFN_DOWN(dma_aux)); + dma_aux += PAGE_SIZE; + } + } else { + for (i = 0; i < alloc->nr_pages; i++) { +#ifdef CONFIG_OUTER_CACHE + unsigned long pfn; +#endif + alloc->pages[i] = alloc_page(GFP_KERNEL); + if (NULL == alloc->pages[i]) { + dev_err(te_device.this_device, + "%s: couldn't alloc page", __func__); + goto no_page; + } + flush_dcache_page(alloc->pages[i]); +#ifdef CONFIG_OUTER_CACHE + pfn = page_to_pfn(alloc->pages[i]); + outer_flush_range(pfn << PAGE_SHIFT, (pfn + 1) << PAGE_SHIFT); +#endif + } + } + + /* alloc ready, let's export it */ + dma_buf = dma_buf_export(alloc, &dma_buf_te_ops, alloc->nr_pages << PAGE_SHIFT, O_CLOEXEC|O_RDWR); + + if (IS_ERR_OR_NULL(dma_buf)) { + dev_err(te_device.this_device, "%s: couldn't export dma_buf", + __func__); + goto no_export; + } + + /* get fd for buf */ + fd = dma_buf_fd(dma_buf, O_CLOEXEC); + + if (fd < 0) { + dev_err(te_device.this_device, + "%s: couldn't get fd from dma_buf", __func__); + goto no_fd; + } + + return fd; + +no_fd: + dma_buf_put(dma_buf); +no_export: + /* i still valid */ +no_page: + if (contiguous) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) + DEFINE_DMA_ATTRS(attrs); + + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); + dma_free_attrs(te_device.this_device, + alloc->nr_pages * PAGE_SIZE, + alloc->contig_cpu_addr, alloc->contig_dma_addr, &attrs); +#else + dma_free_writecombine(te_device.this_device, + alloc->nr_pages * PAGE_SIZE, + alloc->contig_cpu_addr, alloc->contig_dma_addr); +#endif + } else { + while (i-- > 0) + __free_page(alloc->pages[i]); + } +free_page_struct: + kfree(alloc->pages); +free_alloc_object: + kfree(alloc); +no_alloc_object: +zero_size: +no_input: + return -EFAULT; +} + +static int do_dma_buf_te_ioctl_status(struct dma_buf_te_ioctl_status __user *arg) +{ + struct dma_buf_te_ioctl_status status; + struct dma_buf *dmabuf; + struct dma_buf_te_alloc *alloc; + int res = -EINVAL; + + if (copy_from_user(&status, arg, sizeof(status))) + return -EFAULT; + + dmabuf = dma_buf_get(status.fd); + if (IS_ERR_OR_NULL(dmabuf)) + return -EINVAL; + + /* verify it's one of ours */ + if (dmabuf->ops != &dma_buf_te_ops) + goto err_have_dmabuf; + + /* ours, get the current status */ + alloc = dmabuf->priv; + + /* lock while reading status to take a snapshot */ + mutex_lock(&dmabuf->lock); + status.attached_devices = alloc->nr_attached_devices; + status.device_mappings = alloc->nr_device_mappings; + status.cpu_mappings = alloc->nr_cpu_mappings; + mutex_unlock(&dmabuf->lock); + + if (copy_to_user(arg, &status, sizeof(status))) + goto err_have_dmabuf; + + /* All OK */ + res = 0; + +err_have_dmabuf: + dma_buf_put(dmabuf); + return res; +} + +static int do_dma_buf_te_ioctl_set_failing(struct dma_buf_te_ioctl_set_failing __user *arg) +{ + struct dma_buf *dmabuf; + struct dma_buf_te_ioctl_set_failing f; + struct dma_buf_te_alloc * alloc; + int res = -EINVAL; + + if (copy_from_user(&f, arg, sizeof(f))) + return -EFAULT; + + dmabuf = dma_buf_get(f.fd); + if (IS_ERR_OR_NULL(dmabuf)) + return -EINVAL; + + /* verify it's one of ours */ + if (dmabuf->ops != &dma_buf_te_ops) + goto err_have_dmabuf; + + /* ours, set the fail modes */ + alloc = dmabuf->priv; + /* lock to set the fail modes atomically */ + mutex_lock(&dmabuf->lock); + alloc->fail_attach = f.fail_attach; + alloc->fail_map = f.fail_map; + alloc->fail_mmap = f.fail_mmap; + mutex_unlock(&dmabuf->lock); + + /* success */ + res = 0; + +err_have_dmabuf: + dma_buf_put(dmabuf); + return res; +} + +static u32 dma_te_buf_fill(struct dma_buf *dma_buf, unsigned int value) +{ + struct dma_buf_attachment * attachment; + struct sg_table *sgt; + struct scatterlist *sg; + unsigned int count; + unsigned int offset = 0; + int ret = 0; + int i; + + attachment = dma_buf_attach(dma_buf, te_device.this_device); + if (IS_ERR_OR_NULL(attachment)) { + return -EBUSY; + } + + sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(sgt)) { + ret = PTR_ERR(sgt); + goto no_import; + } + + for_each_sg(sgt->sgl, sg, sgt->nents, count) { + ret = dma_buf_begin_cpu_access(dma_buf, offset, sg_dma_len(sg), DMA_BIDIRECTIONAL); + if (ret) + goto no_cpu_access; + for (i = 0; i < sg_dma_len(sg); i = i + PAGE_SIZE) { + void *addr = dma_buf_kmap(dma_buf, i >> PAGE_SHIFT); + memset(addr, value, PAGE_SIZE); + dma_buf_kunmap(dma_buf, i >> PAGE_SHIFT, addr); + } + dma_buf_end_cpu_access(dma_buf, offset, sg_dma_len(sg), DMA_BIDIRECTIONAL); + offset += sg_dma_len(sg); + } + +no_cpu_access: + dma_buf_unmap_attachment(attachment, sgt, DMA_BIDIRECTIONAL); +no_import: + dma_buf_detach(dma_buf, attachment); + return ret; +} + +static int do_dma_buf_te_ioctl_fill(struct dma_buf_te_ioctl_fill __user *arg){ + + struct dma_buf *dmabuf; + struct dma_buf_te_ioctl_fill f; + int ret; + + if (copy_from_user(&f, arg, sizeof(f))) + return -EFAULT; + + dmabuf = dma_buf_get(f.fd); + if (IS_ERR_OR_NULL(dmabuf)) { + return -EINVAL; + } + + ret = dma_te_buf_fill(dmabuf, f.value); + dma_buf_put(dmabuf); + + return ret; +} + +static long dma_buf_te_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case DMA_BUF_TE_VERSION: + return do_dma_buf_te_ioctl_version((struct dma_buf_te_ioctl_version __user *)arg); + case DMA_BUF_TE_ALLOC: + return do_dma_buf_te_ioctl_alloc((struct dma_buf_te_ioctl_alloc __user *)arg, false); + case DMA_BUF_TE_ALLOC_CONT: + return do_dma_buf_te_ioctl_alloc((struct dma_buf_te_ioctl_alloc __user *)arg, true); + case DMA_BUF_TE_QUERY: + return do_dma_buf_te_ioctl_status((struct dma_buf_te_ioctl_status __user *)arg); + case DMA_BUF_TE_SET_FAILING: + return do_dma_buf_te_ioctl_set_failing((struct dma_buf_te_ioctl_set_failing __user *)arg); + case DMA_BUF_TE_FILL: + return do_dma_buf_te_ioctl_fill((struct dma_buf_te_ioctl_fill __user *)arg); + default: + return -ENOTTY; + } +} + +static struct file_operations dma_buf_te_fops = +{ + .owner = THIS_MODULE, + .unlocked_ioctl = dma_buf_te_ioctl, + .compat_ioctl = dma_buf_te_ioctl, +}; + +static int __init dma_buf_te_init(void) +{ + int res; + te_device.minor = MISC_DYNAMIC_MINOR; + te_device.name = "dma_buf_te"; + te_device.fops = &dma_buf_te_fops; + + res = misc_register(&te_device); + if (res) { + printk("Misc device registration failed of 'dma_buf_te'\n"); + return res; + } + te_device.this_device->coherent_dma_mask = DMA_BIT_MASK(32); + + dev_info(te_device.this_device, "dma_buf_te ready\n"); + return 0; + +} + +static void __exit dma_buf_te_exit(void) +{ + misc_deregister(&te_device); +} + +module_init(dma_buf_te_init); +module_exit(dma_buf_te_exit); +MODULE_LICENSE("GPL"); + diff --git a/drivers/base/dma_buf_test_exporter/sconscript b/drivers/base/dma_buf_test_exporter/sconscript new file mode 100755 index 00000000000..bfb8a99f9d4 --- /dev/null +++ b/drivers/base/dma_buf_test_exporter/sconscript @@ -0,0 +1,36 @@ +# +# (C) COPYRIGHT 2010-2013 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + + +import os +Import('env') + +if env['v'] != '1': + env['MAKECOMSTR'] = '[MAKE] ${SOURCE.dir}' + +src = [Glob('#kernel/drivers/base/dma_buf_test_exporter/*.c'), Glob('#kernel/include/linux/*.h'), Glob('#kernel/drivers/base/dma_buf_test_exporter/K*')] + +env.Append( CPPPATH = '#kernel/include' ) + +if env.GetOption('clean') : + env.Execute(Action("make clean", '[CLEAN] dma-buf-test-exporter')) + cmd = env.Command('$STATIC_LIB_PATH/dma-buf-test-exporter.ko', src, []) + env.ProgTarget('dma-buf-test-exporter', cmd) +else: + makeAction=Action("cd ${SOURCE.dir} && make && ( ( [ -f dma-buf-test-exporter.ko ] && cp dma-buf-test-exporter.ko $STATIC_LIB_PATH/ ) || touch $STATIC_LIB_PATH/dma-buf-test-exporter.ko)", '$MAKECOMSTR') + cmd = env.Command('$STATIC_LIB_PATH/dma-buf-test-exporter.ko', src, [makeAction]) + env.ProgTarget('dma-buf-test-exporter', cmd) + + diff --git a/include/linux/dma-buf-test-exporter.h b/include/linux/dma-buf-test-exporter.h new file mode 100755 index 00000000000..db0e9389813 --- /dev/null +++ b/include/linux/dma-buf-test-exporter.h @@ -0,0 +1,78 @@ +/* + * + * (C) COPYRIGHT ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + + + +#ifndef _LINUX_DMA_BUF_TEST_EXPORTER_H_ +#define _LINUX_DMA_BUF_TEST_EXPORTER_H_ + +#include +#include + +#define DMA_BUF_TE_VER_MAJOR 1 +#define DMA_BUF_TE_VER_MINOR 0 +#define DMA_BUF_TE_ENQ 0x642d7465 +#define DMA_BUF_TE_ACK 0x68692100 + +struct dma_buf_te_ioctl_version +{ + int op; /**< Must be set to DMA_BUF_TE_ENQ by client, driver will set it to DMA_BUF_TE_ACK */ + int major; /**< Major version */ + int minor; /**< Minor version */ +}; + +struct dma_buf_te_ioctl_alloc +{ + __u64 size; /* size of buffer to allocate, in pages */ +}; + +struct dma_buf_te_ioctl_status +{ + /* in */ + int fd; /* the dma_buf to query, only dma_buf objects exported by this driver is supported */ + /* out */ + int attached_devices; /* number of devices attached (active 'dma_buf_attach's) */ + int device_mappings; /* number of device mappings (active 'dma_buf_map_attachment's) */ + int cpu_mappings; /* number of cpu mappings (active 'mmap's) */ +}; + +struct dma_buf_te_ioctl_set_failing +{ + /* in */ + int fd; /* the dma_buf to set failure mode for, only dma_buf objects exported by this driver is supported */ + + /* zero = no fail injection, non-zero = inject failure */ + int fail_attach; + int fail_map; + int fail_mmap; +}; + +struct dma_buf_te_ioctl_fill +{ + int fd; + unsigned int value; +}; + +#define DMA_BUF_TE_IOCTL_BASE 'E' +/* Below all returning 0 if successful or -errcode except DMA_BUF_TE_ALLOC which will return fd or -errcode */ +#define DMA_BUF_TE_VERSION _IOR(DMA_BUF_TE_IOCTL_BASE, 0x00, struct dma_buf_te_ioctl_version) +#define DMA_BUF_TE_ALLOC _IOR(DMA_BUF_TE_IOCTL_BASE, 0x01, struct dma_buf_te_ioctl_alloc) +#define DMA_BUF_TE_QUERY _IOR(DMA_BUF_TE_IOCTL_BASE, 0x02, struct dma_buf_te_ioctl_status) +#define DMA_BUF_TE_SET_FAILING _IOW(DMA_BUF_TE_IOCTL_BASE, 0x03, struct dma_buf_te_ioctl_set_failing) +#define DMA_BUF_TE_ALLOC_CONT _IOR(DMA_BUF_TE_IOCTL_BASE, 0x04, struct dma_buf_te_ioctl_alloc) +#define DMA_BUF_TE_FILL _IOR(DMA_BUF_TE_IOCTL_BASE, 0x05, struct dma_buf_te_ioctl_fill) + +#endif /* _LINUX_DMA_BUF_TEST_EXPORTER_H_ */ -- cgit v1.2.3