1 // SPDX-License-Identifier: GPL-2.0
3 * Framework for userspace DMA-BUF allocations
5 * Copyright (C) 2011 Google, Inc.
6 * Copyright (C) 2019 Linaro Ltd.
9 #include <linux/cdev.h>
10 #include <linux/debugfs.h>
11 #include <linux/device.h>
12 #include <linux/dma-buf.h>
13 #include <linux/err.h>
14 #include <linux/xarray.h>
15 #include <linux/list.h>
16 #include <linux/slab.h>
17 #include <linux/uaccess.h>
18 #include <linux/syscalls.h>
19 #include <linux/dma-heap.h>
20 #include <uapi/linux/dma-heap.h>
22 #define DEVNAME "dma_heap"
24 #define NUM_HEAP_MINORS 128
27 * struct dma_heap - represents a dmabuf heap in the system
28 * @name: used for debugging/device-node name
29 * @ops: ops struct for this heap
30 * @heap_devt heap device node
31 * @list list head connecting to list of heaps
32 * @heap_cdev heap char device
34 * Represents a heap of memory from which buffers can be made.
38 const struct dma_heap_ops
*ops
;
41 struct list_head list
;
42 struct cdev heap_cdev
;
45 static LIST_HEAD(heap_list
);
46 static DEFINE_MUTEX(heap_list_lock
);
47 static dev_t dma_heap_devt
;
48 static struct class *dma_heap_class
;
49 static DEFINE_XARRAY_ALLOC(dma_heap_minors
);
51 static int dma_heap_buffer_alloc(struct dma_heap
*heap
, size_t len
,
52 unsigned int fd_flags
,
53 unsigned int heap_flags
)
56 * Allocations from all heaps have to begin
57 * and end on page boundaries.
59 len
= PAGE_ALIGN(len
);
63 return heap
->ops
->allocate(heap
, len
, fd_flags
, heap_flags
);
66 static int dma_heap_open(struct inode
*inode
, struct file
*file
)
68 struct dma_heap
*heap
;
70 heap
= xa_load(&dma_heap_minors
, iminor(inode
));
72 pr_err("dma_heap: minor %d unknown.\n", iminor(inode
));
76 /* instance data as context */
77 file
->private_data
= heap
;
78 nonseekable_open(inode
, file
);
83 static long dma_heap_ioctl_allocate(struct file
*file
, void *data
)
85 struct dma_heap_allocation_data
*heap_allocation
= data
;
86 struct dma_heap
*heap
= file
->private_data
;
89 if (heap_allocation
->fd
)
92 if (heap_allocation
->fd_flags
& ~DMA_HEAP_VALID_FD_FLAGS
)
95 if (heap_allocation
->heap_flags
& ~DMA_HEAP_VALID_HEAP_FLAGS
)
98 fd
= dma_heap_buffer_alloc(heap
, heap_allocation
->len
,
99 heap_allocation
->fd_flags
,
100 heap_allocation
->heap_flags
);
104 heap_allocation
->fd
= fd
;
109 static unsigned int dma_heap_ioctl_cmds
[] = {
110 DMA_HEAP_IOCTL_ALLOC
,
113 static long dma_heap_ioctl(struct file
*file
, unsigned int ucmd
,
116 char stack_kdata
[128];
117 char *kdata
= stack_kdata
;
119 unsigned int in_size
, out_size
, drv_size
, ksize
;
120 int nr
= _IOC_NR(ucmd
);
123 if (nr
>= ARRAY_SIZE(dma_heap_ioctl_cmds
))
126 /* Get the kernel ioctl cmd that matches */
127 kcmd
= dma_heap_ioctl_cmds
[nr
];
129 /* Figure out the delta between user cmd size and kernel cmd size */
130 drv_size
= _IOC_SIZE(kcmd
);
131 out_size
= _IOC_SIZE(ucmd
);
133 if ((ucmd
& kcmd
& IOC_IN
) == 0)
135 if ((ucmd
& kcmd
& IOC_OUT
) == 0)
137 ksize
= max(max(in_size
, out_size
), drv_size
);
139 /* If necessary, allocate buffer for ioctl argument */
140 if (ksize
> sizeof(stack_kdata
)) {
141 kdata
= kmalloc(ksize
, GFP_KERNEL
);
146 if (copy_from_user(kdata
, (void __user
*)arg
, in_size
) != 0) {
151 /* zero out any difference between the kernel/user structure size */
153 memset(kdata
+ in_size
, 0, ksize
- in_size
);
156 case DMA_HEAP_IOCTL_ALLOC
:
157 ret
= dma_heap_ioctl_allocate(file
, kdata
);
164 if (copy_to_user((void __user
*)arg
, kdata
, out_size
) != 0)
167 if (kdata
!= stack_kdata
)
172 static const struct file_operations dma_heap_fops
= {
173 .owner
= THIS_MODULE
,
174 .open
= dma_heap_open
,
175 .unlocked_ioctl
= dma_heap_ioctl
,
177 .compat_ioctl
= dma_heap_ioctl
,
182 * dma_heap_get_drvdata() - get per-subdriver data for the heap
183 * @heap: DMA-Heap to retrieve private data for
186 * The per-subdriver data for the heap.
188 void *dma_heap_get_drvdata(struct dma_heap
*heap
)
193 struct dma_heap
*dma_heap_add(const struct dma_heap_export_info
*exp_info
)
195 struct dma_heap
*heap
, *h
, *err_ret
;
196 struct device
*dev_ret
;
200 if (!exp_info
->name
|| !strcmp(exp_info
->name
, "")) {
201 pr_err("dma_heap: Cannot add heap without a name\n");
202 return ERR_PTR(-EINVAL
);
205 if (!exp_info
->ops
|| !exp_info
->ops
->allocate
) {
206 pr_err("dma_heap: Cannot add heap with invalid ops struct\n");
207 return ERR_PTR(-EINVAL
);
210 /* check the name is unique */
211 mutex_lock(&heap_list_lock
);
212 list_for_each_entry(h
, &heap_list
, list
) {
213 if (!strcmp(h
->name
, exp_info
->name
)) {
214 mutex_unlock(&heap_list_lock
);
215 pr_err("dma_heap: Already registered heap named %s\n",
217 return ERR_PTR(-EINVAL
);
220 mutex_unlock(&heap_list_lock
);
222 heap
= kzalloc(sizeof(*heap
), GFP_KERNEL
);
224 return ERR_PTR(-ENOMEM
);
226 heap
->name
= exp_info
->name
;
227 heap
->ops
= exp_info
->ops
;
228 heap
->priv
= exp_info
->priv
;
230 /* Find unused minor number */
231 ret
= xa_alloc(&dma_heap_minors
, &minor
, heap
,
232 XA_LIMIT(0, NUM_HEAP_MINORS
- 1), GFP_KERNEL
);
234 pr_err("dma_heap: Unable to get minor number for heap\n");
235 err_ret
= ERR_PTR(ret
);
240 heap
->heap_devt
= MKDEV(MAJOR(dma_heap_devt
), minor
);
242 cdev_init(&heap
->heap_cdev
, &dma_heap_fops
);
243 ret
= cdev_add(&heap
->heap_cdev
, heap
->heap_devt
, 1);
245 pr_err("dma_heap: Unable to add char device\n");
246 err_ret
= ERR_PTR(ret
);
250 dev_ret
= device_create(dma_heap_class
,
255 if (IS_ERR(dev_ret
)) {
256 pr_err("dma_heap: Unable to create device\n");
257 err_ret
= ERR_CAST(dev_ret
);
260 /* Add heap to the list */
261 mutex_lock(&heap_list_lock
);
262 list_add(&heap
->list
, &heap_list
);
263 mutex_unlock(&heap_list_lock
);
268 cdev_del(&heap
->heap_cdev
);
270 xa_erase(&dma_heap_minors
, minor
);
276 static char *dma_heap_devnode(struct device
*dev
, umode_t
*mode
)
278 return kasprintf(GFP_KERNEL
, "dma_heap/%s", dev_name(dev
));
281 static int dma_heap_init(void)
285 ret
= alloc_chrdev_region(&dma_heap_devt
, 0, NUM_HEAP_MINORS
, DEVNAME
);
289 dma_heap_class
= class_create(THIS_MODULE
, DEVNAME
);
290 if (IS_ERR(dma_heap_class
)) {
291 unregister_chrdev_region(dma_heap_devt
, NUM_HEAP_MINORS
);
292 return PTR_ERR(dma_heap_class
);
294 dma_heap_class
->devnode
= dma_heap_devnode
;
298 subsys_initcall(dma_heap_init
);