Add a generic way for userspace to allocate dma-buf's for SPI transfers. Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> --- drivers/dma-buf/Makefile | 2 +- drivers/dma-buf/dev.c | 211 +++++++++++++++++++++++++++++++++++++++ include/uapi/linux/dma-buf-dev.h | 35 +++++++ 3 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 drivers/dma-buf/dev.c create mode 100644 include/uapi/linux/dma-buf-dev.h diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index 210a10b..ec867f7 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -1,3 +1,3 @@ -obj-y := dma-buf.o fence.o reservation.o seqno-fence.o fence-array.o +obj-y := dma-buf.o fence.o reservation.o seqno-fence.o fence-array.o dev.o obj-$(CONFIG_SYNC_FILE) += sync_file.o obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o diff --git a/drivers/dma-buf/dev.c b/drivers/dma-buf/dev.c new file mode 100644 index 0000000..536d9bf --- /dev/null +++ b/drivers/dma-buf/dev.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/dma-buf.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <uapi/linux/dma-buf-dev.h> + +struct dma_buf_dev_object { + struct device *dev; + unsigned long attrs; + dma_addr_t dma_addr; + void *vaddr; + size_t size; +}; + +static struct sg_table * +dma_buf_dev_map_dma_buf(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct dma_buf_dev_object *obj = attach->dmabuf->priv; + struct sg_table *sgt; + int ret; + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return ERR_PTR(-ENOMEM); + + ret = dma_get_sgtable(obj->dev, sgt, obj->vaddr, + obj->dma_addr, obj->size); + if (ret < 0) + goto err_free; + + if (!dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir)) { + ret = -ENOMEM; + goto err_free_table; + } + + return sgt; + +err_free_table: + sg_free_table(sgt); +err_free: + kfree(sgt); + + return ERR_PTR(ret); +} + +static void dma_buf_dev_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); + sg_free_table(sgt); + kfree(sgt); +} + +static void dma_buf_dev_release(struct dma_buf *dma_buf) +{ + struct dma_buf_dev_object *obj = dma_buf->priv; + +/* FIXME remove */ +pr_info("%s()\n", __func__); + dma_free_attrs(obj->dev, obj->size, obj->vaddr, obj->dma_addr, + obj->attrs); + kfree(obj); +} + +static void *dma_buf_dev_kmap(struct dma_buf *dma_buf, unsigned long page_num) +{ + struct dma_buf_dev_object *obj = dma_buf->priv; + + return obj->vaddr + page_num * PAGE_SIZE; +} + +static void *dma_buf_dev_vmap(struct dma_buf *dma_buf) +{ + struct dma_buf_dev_object *obj = dma_buf->priv; + + return obj->vaddr; +} + +static int dma_buf_dev_mmap(struct dma_buf *dma_buf, + struct vm_area_struct *vma) +{ + struct dma_buf_dev_object *obj = dma_buf->priv; + int ret; + + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; + + ret = dma_mmap_attrs(obj->dev, vma, obj->vaddr, obj->dma_addr, + vma->vm_end - vma->vm_start, obj->attrs); + + return ret; +} + +static const struct dma_buf_ops dma_buf_dev_ops = { + .map_dma_buf = dma_buf_dev_map_dma_buf, + .unmap_dma_buf = dma_buf_dev_unmap_dma_buf, + .release = dma_buf_dev_release, + .kmap_atomic = dma_buf_dev_kmap, + .kmap = dma_buf_dev_kmap, + .vmap = dma_buf_dev_vmap, + .mmap = dma_buf_dev_mmap, +}; + +struct dma_buf *dma_buf_dev_alloc_attrs(struct device *dev, size_t size, + unsigned long attrs, int flags) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf_dev_object *obj; + struct dma_buf *dmabuf; + int ret; + + if (flags & ~(O_CLOEXEC | O_ACCMODE)) + return ERR_PTR(-EINVAL); + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return ERR_PTR(-ENOMEM); + + obj->dev = dev; + obj->size = size; + obj->attrs = attrs; + + obj->vaddr = dma_alloc_attrs(dev, size, &obj->dma_addr, GFP_KERNEL, attrs); + if (!obj->vaddr) { + ret = -ENOMEM; + goto err_free_obj; + } + + exp_info.ops = &dma_buf_dev_ops; + exp_info.size = obj->size; + exp_info.flags = flags; + exp_info.priv = obj; + + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + goto err_free_buf; + } + + return dmabuf; + +err_free_buf: + dma_free_attrs(dev, size, obj->vaddr, obj->dma_addr, attrs); +err_free_obj: + kfree(obj); + + return ERR_PTR(ret); +} + +static long dma_buf_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct dma_buf_dev_create create; + struct dma_buf *dmabuf; + + switch (cmd) { + case DMA_BUF_DEV_IOCTL_CREATE: + + if (copy_from_user(&create, (void __user *)arg, sizeof(create))) + return -EFAULT; + + if (!create.size) + return -EINVAL; + + dmabuf = dma_buf_dev_alloc_attrs(NULL, create.size, + create.attrs, create.flags); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + create.fd = dma_buf_fd(dmabuf, create.flags); + if (create.fd < 0) { + dma_buf_put(dmabuf); + return create.fd; + } + + if (copy_to_user((void __user *)arg, &create, sizeof(create))) + return -EFAULT; + + return 0; + default: + return -ENOTTY; + } +} + +static const struct file_operations dma_buf_dev_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dma_buf_dev_ioctl, + .compat_ioctl = dma_buf_dev_ioctl, +}; + +static struct miscdevice dma_buf_dev_misc = { + .fops = &dma_buf_dev_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = "dma-buf", +}; +module_misc_device(dma_buf_dev_misc); + +MODULE_AUTHOR("Noralf Trønnes"); +MODULE_DESCRIPTION("User mode dma-buf creation"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/dma-buf-dev.h b/include/uapi/linux/dma-buf-dev.h new file mode 100644 index 0000000..fddbe04 --- /dev/null +++ b/include/uapi/linux/dma-buf-dev.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _DMA_BUF_DEV_UAPI_H_ +#define _DMA_BUF_DEV_UAPI_H_ + +#include <linux/types.h> + +/** + * struct dma_buf_dev_create - + * @attrs: Attributes of mapping properties requested in dma_alloc_attrs + * @size: Buffer size + * @flags: Mode flags for the dma-buf file + * @fd: Returned dma-buf file descriptor + */ +struct dma_buf_dev_create { + __u64 attrs; +#define DMA_BUF_DEV_ATTR_WRITE_COMBINE BIT(2) + __u64 size; + __u64 flags; + + __s64 fd; +}; + +/* FIXME: Update Documentation/ioctl/ioctl-number.txt */ +#define DMA_BUF_DEV_BASE 0xB6 +#define DMA_BUF_DEV_IOCTL_CREATE _IOWR(DMA_BUF_DEV_BASE, 0, struct dma_buf_dev_create) + +#endif -- 2.10.2 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel