This is the core of simple allocator module. It aim to offert one common ioctl to allocate specific memory.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/simpleallocator/Kconfig | 10 ++ drivers/simpleallocator/Makefile | 1 + drivers/simpleallocator/simple-allocator-priv.h | 27 ++++ drivers/simpleallocator/simple-allocator.c | 167 ++++++++++++++++++++++++ include/uapi/linux/simple-allocator.h | 35 +++++ 7 files changed, 243 insertions(+) create mode 100644 drivers/simpleallocator/Kconfig create mode 100644 drivers/simpleallocator/Makefile create mode 100644 drivers/simpleallocator/simple-allocator-priv.h create mode 100644 drivers/simpleallocator/simple-allocator.c create mode 100644 include/uapi/linux/simple-allocator.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index e1e2066..a6d8828 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
source "drivers/fpga/Kconfig"
+source "drivers/simpleallocator/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 060026a..5081eb8 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -173,3 +173,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_NVMEM) += nvmem/ obj-$(CONFIG_FPGA) += fpga/ +obj-$(CONFIG_SIMPLE_ALLOCATOR) += simpleallocator/ diff --git a/drivers/simpleallocator/Kconfig b/drivers/simpleallocator/Kconfig new file mode 100644 index 0000000..c6fc2e3 --- /dev/null +++ b/drivers/simpleallocator/Kconfig @@ -0,0 +1,10 @@ +menu "Simple Allocator" + +config SIMPLE_ALLOCATOR + tristate "Simple Alllocator Framework" + select DMA_SHARED_BUFFER + ---help--- + The Simple Allocator Framework adds an API to allocate and share + memory in userland. + +endmenu diff --git a/drivers/simpleallocator/Makefile b/drivers/simpleallocator/Makefile new file mode 100644 index 0000000..e27c6ad --- /dev/null +++ b/drivers/simpleallocator/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SIMPLE_ALLOCATOR) += simple-allocator.o diff --git a/drivers/simpleallocator/simple-allocator-priv.h b/drivers/simpleallocator/simple-allocator-priv.h new file mode 100644 index 0000000..b360bb1 --- /dev/null +++ b/drivers/simpleallocator/simple-allocator-priv.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) Linaro 2016 + * + * Author: Benjamin Gaignard benjamin.gaignard@linaro.org + * + * License terms: GNU General Public License (GPL) + */ + +#ifndef _SIMPLE_ALLOCATOR_PRIV_H_ +#define _SIMPLE_ALLOCATOR_PRIV_H_ + +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/dma-buf.h> + +struct sa_device { + struct device dev; + struct cdev chrdev; + struct module *owner; + const char *name; + struct dma_buf *(*allocate)(struct sa_device *, u64 length, u32 flags); +}; + +int simple_allocator_register(struct sa_device *sadev); +void simple_allocator_unregister(struct sa_device *sadev); + +#endif diff --git a/drivers/simpleallocator/simple-allocator.c b/drivers/simpleallocator/simple-allocator.c new file mode 100644 index 0000000..a76b593 --- /dev/null +++ b/drivers/simpleallocator/simple-allocator.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) Linaro 2016 + * + * Author: Benjamin Gaignard benjamin.gaignard@linaro.org + * + * License terms: GNU General Public License (GPL) + */ + +#include <linux/module.h> +#include <linux/simple-allocator.h> +#include <linux/uaccess.h> + +#include "simple-allocator-priv.h" + +#define SA_MAJOR 222 +#define SA_NUM_DEVICES 256 +#define SA_NAME "simple_allocator" + +static int sa_minor; + +static struct class sa_class = { + .name = SA_NAME, +}; + +static long sa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct sa_device *sadev = filp->private_data; + int ret = -ENODEV; + + switch (cmd) { + case SA_IOC_ALLOC: + { + struct simple_allocate_data data; + struct dma_buf *dmabuf; + + if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) + return -EFAULT; + + if (data.version != 0) + return -EINVAL; + + dmabuf = sadev->allocate(sadev, data.length, data.flags); + if (!dmabuf) + return -EINVAL; + + data.fd = dma_buf_fd(dmabuf, data.flags); + if (data.fd < 0) { + dma_buf_put(dmabuf); + return -EINVAL; + } + + data.length = dmabuf->size; + + if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) { + dma_buf_put(dmabuf); + return -EFAULT; + } + + return 0; + } + } + return ret; +} + +static int sa_open(struct inode *inode, struct file *filp) +{ + struct sa_device *sadev = container_of(inode->i_cdev, + struct sa_device, chrdev); + + if (!sadev) + return -ENODEV; + + get_device(&sadev->dev); + filp->private_data = sadev; + return 0; +} + +static int sa_release(struct inode *inode, struct file *filp) +{ + struct sa_device *sadev = container_of(inode->i_cdev, + struct sa_device, chrdev); + + if (!sadev) + return -ENODEV; + + put_device(&sadev->dev); + return 0; +} + +static const struct file_operations sa_fops = { + .owner = THIS_MODULE, + .open = sa_open, + .release = sa_release, + .unlocked_ioctl = sa_ioctl, +}; + +int simple_allocator_register(struct sa_device *sadev) +{ + int ret; + + cdev_init(&sadev->chrdev, &sa_fops); + sadev->chrdev.owner = sadev->owner; + + ret = cdev_add(&sadev->chrdev, MKDEV(SA_MAJOR, sa_minor), 1); + if (ret < 0) + return ret; + + sadev->dev.class = &sa_class; + sadev->dev.devt = MKDEV(SA_MAJOR, sa_minor); + dev_set_name(&sadev->dev, "%s%d", sadev->name, sa_minor); + ret = device_register(&sadev->dev); + if (ret < 0) + goto cleanup; + + sa_minor++; + return 0; + +cleanup: + cdev_del(&sadev->chrdev); + return ret; +} +EXPORT_SYMBOL_GPL(simple_allocator_register); + +void simple_allocator_unregister(struct sa_device *sadev) +{ + if (!sadev) + return; + + cdev_del(&sadev->chrdev); + device_del(&sadev->dev); + put_device(&sadev->dev); +} +EXPORT_SYMBOL_GPL(simple_allocator_unregister); + +static int __init sa_init(void) +{ + dev_t dev = MKDEV(SA_MAJOR, 0); + int ret; + + ret = register_chrdev_region(dev, SA_NUM_DEVICES, SA_NAME); + if (ret < 0) + return ret; + + ret = class_register(&sa_class); + if (ret < 0) { + unregister_chrdev_region(dev, SA_NUM_DEVICES); + return -EIO; + } + + return 0; +} + +static void __exit sa_exit(void) +{ + dev_t dev = MKDEV(SA_MAJOR, 0); + + class_unregister(&sa_class); + unregister_chrdev_region(dev, SA_NUM_DEVICES); +} + +subsys_initcall(sa_init); +module_exit(sa_exit); + +MODULE_AUTHOR("Benjamin Gaignard benjamin.gaignard@linaro.org"); +MODULE_DESCRIPTION("Simple allocator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(SA_MAJOR); diff --git a/include/uapi/linux/simple-allocator.h b/include/uapi/linux/simple-allocator.h new file mode 100644 index 0000000..5520a85 --- /dev/null +++ b/include/uapi/linux/simple-allocator.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Linaro 2016 + * + * Author: Benjamin Gaignard benjamin.gaignard@linaro.org + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _SIMPLE_ALLOCATOR_H_ +#define _SIMPLE_ALLOCATOR_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> + +/** + * struct simple_allocate_data - allocation parameters + * @version: structure version (must be set to 0) + * @length: size of the requested buffer + * @flags: mode flags for the file like O_RDWR or O_CLOEXEC + * @fd: returned file descriptor + */ +struct simple_allocate_data { + __u64 version; + __u64 length; + __u32 flags; + __u32 reserved1; + __s32 fd; + __u32 reserved2; +}; + +#define SA_IOC_MAGIC 'S' + +#define SA_IOC_ALLOC _IOWR(SA_IOC_MAGIC, 0, struct simple_allocate_data) + +#endif