Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Framework for userspace DMA-BUF allocations
  4 *
  5 * Copyright (C) 2011 Google, Inc.
  6 * Copyright (C) 2019 Linaro Ltd.
  7 */
  8
  9#include <linux/cdev.h>
 10#include <linux/device.h>
 11#include <linux/dma-buf.h>
 12#include <linux/dma-heap.h>
 13#include <linux/err.h>
 14#include <linux/list.h>
 15#include <linux/nospec.h>
 16#include <linux/syscalls.h>
 17#include <linux/uaccess.h>
 18#include <linux/xarray.h>
 19#include <uapi/linux/dma-heap.h>
 20
 21#define DEVNAME "dma_heap"
 22
 23#define NUM_HEAP_MINORS 128
 24
 25/**
 26 * struct dma_heap - represents a dmabuf heap in the system
 27 * @name:		used for debugging/device-node name
 28 * @ops:		ops struct for this heap
 29 * @priv:		private data for this heap
 30 * @heap_devt:		heap device node
 31 * @list:		list head connecting to list of heaps
 32 * @heap_cdev:		heap char device
 33 *
 34 * Represents a heap of memory from which buffers can be made.
 35 */
 36struct dma_heap {
 37	const char *name;
 38	const struct dma_heap_ops *ops;
 39	void *priv;
 40	dev_t heap_devt;
 41	struct list_head list;
 42	struct cdev heap_cdev;
 43};
 44
 45static LIST_HEAD(heap_list);
 46static DEFINE_MUTEX(heap_list_lock);
 47static dev_t dma_heap_devt;
 48static struct class *dma_heap_class;
 49static DEFINE_XARRAY_ALLOC(dma_heap_minors);
 50
 51static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
 52				 u32 fd_flags,
 53				 u64 heap_flags)
 54{
 55	struct dma_buf *dmabuf;
 56	int fd;
 57
 58	/*
 59	 * Allocations from all heaps have to begin
 60	 * and end on page boundaries.
 61	 */
 62	len = PAGE_ALIGN(len);
 63	if (!len)
 64		return -EINVAL;
 65
 66	dmabuf = heap->ops->allocate(heap, len, fd_flags, heap_flags);
 67	if (IS_ERR(dmabuf))
 68		return PTR_ERR(dmabuf);
 69
 70	fd = dma_buf_fd(dmabuf, fd_flags);
 71	if (fd < 0) {
 72		dma_buf_put(dmabuf);
 73		/* just return, as put will call release and that will free */
 74	}
 75	return fd;
 76}
 77
 78static int dma_heap_open(struct inode *inode, struct file *file)
 79{
 80	struct dma_heap *heap;
 81
 82	heap = xa_load(&dma_heap_minors, iminor(inode));
 83	if (!heap) {
 84		pr_err("dma_heap: minor %d unknown.\n", iminor(inode));
 85		return -ENODEV;
 86	}
 87
 88	/* instance data as context */
 89	file->private_data = heap;
 90	nonseekable_open(inode, file);
 91
 92	return 0;
 93}
 94
 95static long dma_heap_ioctl_allocate(struct file *file, void *data)
 96{
 97	struct dma_heap_allocation_data *heap_allocation = data;
 98	struct dma_heap *heap = file->private_data;
 99	int fd;
100
101	if (heap_allocation->fd)
102		return -EINVAL;
103
104	if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
105		return -EINVAL;
106
107	if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
108		return -EINVAL;
109
110	fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
111				   heap_allocation->fd_flags,
112				   heap_allocation->heap_flags);
113	if (fd < 0)
114		return fd;
115
116	heap_allocation->fd = fd;
117
118	return 0;
119}
120
121static unsigned int dma_heap_ioctl_cmds[] = {
122	DMA_HEAP_IOCTL_ALLOC,
123};
124
125static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
126			   unsigned long arg)
127{
128	char stack_kdata[128];
129	char *kdata = stack_kdata;
130	unsigned int kcmd;
131	unsigned int in_size, out_size, drv_size, ksize;
132	int nr = _IOC_NR(ucmd);
133	int ret = 0;
134
135	if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
136		return -EINVAL;
137
138	nr = array_index_nospec(nr, ARRAY_SIZE(dma_heap_ioctl_cmds));
139	/* Get the kernel ioctl cmd that matches */
140	kcmd = dma_heap_ioctl_cmds[nr];
141
142	/* Figure out the delta between user cmd size and kernel cmd size */
143	drv_size = _IOC_SIZE(kcmd);
144	out_size = _IOC_SIZE(ucmd);
145	in_size = out_size;
146	if ((ucmd & kcmd & IOC_IN) == 0)
147		in_size = 0;
148	if ((ucmd & kcmd & IOC_OUT) == 0)
149		out_size = 0;
150	ksize = max(max(in_size, out_size), drv_size);
151
152	/* If necessary, allocate buffer for ioctl argument */
153	if (ksize > sizeof(stack_kdata)) {
154		kdata = kmalloc(ksize, GFP_KERNEL);
155		if (!kdata)
156			return -ENOMEM;
157	}
158
159	if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
160		ret = -EFAULT;
161		goto err;
162	}
163
164	/* zero out any difference between the kernel/user structure size */
165	if (ksize > in_size)
166		memset(kdata + in_size, 0, ksize - in_size);
167
168	switch (kcmd) {
169	case DMA_HEAP_IOCTL_ALLOC:
170		ret = dma_heap_ioctl_allocate(file, kdata);
171		break;
172	default:
173		ret = -ENOTTY;
174		goto err;
175	}
176
177	if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
178		ret = -EFAULT;
179err:
180	if (kdata != stack_kdata)
181		kfree(kdata);
182	return ret;
183}
184
185static const struct file_operations dma_heap_fops = {
186	.owner          = THIS_MODULE,
187	.open		= dma_heap_open,
188	.unlocked_ioctl = dma_heap_ioctl,
189#ifdef CONFIG_COMPAT
190	.compat_ioctl	= dma_heap_ioctl,
191#endif
192};
193
194/**
195 * dma_heap_get_drvdata - get per-heap driver data
196 * @heap: DMA-Heap to retrieve private data for
197 *
198 * Returns:
199 * The per-heap data for the heap.
200 */
201void *dma_heap_get_drvdata(struct dma_heap *heap)
202{
203	return heap->priv;
204}
205
206/**
207 * dma_heap_get_name - get heap name
208 * @heap: DMA-Heap to retrieve the name of
209 *
210 * Returns:
211 * The char* for the heap name.
212 */
213const char *dma_heap_get_name(struct dma_heap *heap)
214{
215	return heap->name;
216}
217
218/**
219 * dma_heap_add - adds a heap to dmabuf heaps
220 * @exp_info: information needed to register this heap
221 */
222struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
223{
224	struct dma_heap *heap, *h, *err_ret;
225	struct device *dev_ret;
226	unsigned int minor;
227	int ret;
228
229	if (!exp_info->name || !strcmp(exp_info->name, "")) {
230		pr_err("dma_heap: Cannot add heap without a name\n");
231		return ERR_PTR(-EINVAL);
232	}
233
234	if (!exp_info->ops || !exp_info->ops->allocate) {
235		pr_err("dma_heap: Cannot add heap with invalid ops struct\n");
236		return ERR_PTR(-EINVAL);
237	}
238
239	heap = kzalloc(sizeof(*heap), GFP_KERNEL);
240	if (!heap)
241		return ERR_PTR(-ENOMEM);
242
243	heap->name = exp_info->name;
244	heap->ops = exp_info->ops;
245	heap->priv = exp_info->priv;
246
247	/* Find unused minor number */
248	ret = xa_alloc(&dma_heap_minors, &minor, heap,
249		       XA_LIMIT(0, NUM_HEAP_MINORS - 1), GFP_KERNEL);
250	if (ret < 0) {
251		pr_err("dma_heap: Unable to get minor number for heap\n");
252		err_ret = ERR_PTR(ret);
253		goto err0;
254	}
255
256	/* Create device */
257	heap->heap_devt = MKDEV(MAJOR(dma_heap_devt), minor);
258
259	cdev_init(&heap->heap_cdev, &dma_heap_fops);
260	ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1);
261	if (ret < 0) {
262		pr_err("dma_heap: Unable to add char device\n");
263		err_ret = ERR_PTR(ret);
264		goto err1;
265	}
266
267	dev_ret = device_create(dma_heap_class,
268				NULL,
269				heap->heap_devt,
270				NULL,
271				heap->name);
272	if (IS_ERR(dev_ret)) {
273		pr_err("dma_heap: Unable to create device\n");
274		err_ret = ERR_CAST(dev_ret);
275		goto err2;
276	}
277
278	mutex_lock(&heap_list_lock);
279	/* check the name is unique */
280	list_for_each_entry(h, &heap_list, list) {
281		if (!strcmp(h->name, exp_info->name)) {
282			mutex_unlock(&heap_list_lock);
283			pr_err("dma_heap: Already registered heap named %s\n",
284			       exp_info->name);
285			err_ret = ERR_PTR(-EINVAL);
286			goto err3;
287		}
288	}
289
290	/* Add heap to the list */
291	list_add(&heap->list, &heap_list);
292	mutex_unlock(&heap_list_lock);
293
294	return heap;
295
296err3:
297	device_destroy(dma_heap_class, heap->heap_devt);
298err2:
299	cdev_del(&heap->heap_cdev);
300err1:
301	xa_erase(&dma_heap_minors, minor);
302err0:
303	kfree(heap);
304	return err_ret;
305}
306
307static char *dma_heap_devnode(const struct device *dev, umode_t *mode)
308{
309	return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev));
310}
311
312static int dma_heap_init(void)
313{
314	int ret;
315
316	ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
317	if (ret)
318		return ret;
319
320	dma_heap_class = class_create(DEVNAME);
321	if (IS_ERR(dma_heap_class)) {
322		unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
323		return PTR_ERR(dma_heap_class);
324	}
325	dma_heap_class->devnode = dma_heap_devnode;
326
327	return 0;
328}
329subsys_initcall(dma_heap_init);