3 * Copyright (C) 2013 Google, Inc.
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
16 #define pr_fmt(fmt) "ion-test: " fmt
18 #include <linux/dma-buf.h>
19 #include <linux/dma-direction.h>
21 #include <linux/miscdevice.h>
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/sched.h>
26 #include <linux/slab.h>
27 #include <linux/uaccess.h>
28 #include <linux/vmalloc.h>
31 #include "../uapi/ion_test.h"
33 #define u64_to_uptr(x) ((void __user *)(unsigned long)(x))
35 struct ion_test_device
{
36 struct miscdevice misc
;
39 struct ion_test_data
{
40 struct dma_buf
*dma_buf
;
44 static int ion_handle_test_dma(struct device
*dev
, struct dma_buf
*dma_buf
,
45 void __user
*ptr
, size_t offset
, size_t size
, bool write
)
48 struct dma_buf_attachment
*attach
;
49 struct sg_table
*table
;
50 pgprot_t pgprot
= pgprot_writecombine(PAGE_KERNEL
);
51 enum dma_data_direction dir
= write
? DMA_FROM_DEVICE
: DMA_TO_DEVICE
;
52 struct sg_page_iter sg_iter
;
53 unsigned long offset_page
;
55 attach
= dma_buf_attach(dma_buf
, dev
);
57 return PTR_ERR(attach
);
59 table
= dma_buf_map_attachment(attach
, dir
);
61 return PTR_ERR(table
);
63 offset_page
= offset
>> PAGE_SHIFT
;
66 for_each_sg_page(table
->sgl
, &sg_iter
, table
->nents
, offset_page
) {
67 struct page
*page
= sg_page_iter_page(&sg_iter
);
68 void *vaddr
= vmap(&page
, 1, VM_MAP
, pgprot
);
69 size_t to_copy
= PAGE_SIZE
- offset
;
71 to_copy
= min(to_copy
, size
);
78 ret
= copy_from_user(vaddr
+ offset
, ptr
, to_copy
);
80 ret
= copy_to_user(ptr
, vaddr
+ offset
, to_copy
);
95 dma_buf_unmap_attachment(attach
, table
, dir
);
96 dma_buf_detach(dma_buf
, attach
);
100 static int ion_handle_test_kernel(struct dma_buf
*dma_buf
, void __user
*ptr
,
101 size_t offset
, size_t size
, bool write
)
104 unsigned long page_offset
= offset
>> PAGE_SHIFT
;
105 size_t copy_offset
= offset
% PAGE_SIZE
;
106 size_t copy_size
= size
;
107 enum dma_data_direction dir
= write
? DMA_FROM_DEVICE
: DMA_TO_DEVICE
;
109 if (offset
> dma_buf
->size
|| size
> dma_buf
->size
- offset
)
112 ret
= dma_buf_begin_cpu_access(dma_buf
, offset
, size
, dir
);
116 while (copy_size
> 0) {
118 void *vaddr
= dma_buf_kmap(dma_buf
, page_offset
);
123 to_copy
= min_t(size_t, PAGE_SIZE
- copy_offset
, copy_size
);
126 ret
= copy_from_user(vaddr
+ copy_offset
, ptr
, to_copy
);
128 ret
= copy_to_user(ptr
, vaddr
+ copy_offset
, to_copy
);
130 dma_buf_kunmap(dma_buf
, page_offset
, vaddr
);
136 copy_size
-= to_copy
;
142 dma_buf_end_cpu_access(dma_buf
, offset
, size
, dir
);
146 static long ion_test_ioctl(struct file
*filp
, unsigned int cmd
,
149 struct ion_test_data
*test_data
= filp
->private_data
;
153 struct ion_test_rw_data test_rw
;
156 if (_IOC_SIZE(cmd
) > sizeof(data
))
159 if (_IOC_DIR(cmd
) & _IOC_WRITE
)
160 if (copy_from_user(&data
, (void __user
*)arg
, _IOC_SIZE(cmd
)))
164 case ION_IOC_TEST_SET_FD
:
166 struct dma_buf
*dma_buf
= NULL
;
170 dma_buf
= dma_buf_get((int)arg
);
172 return PTR_ERR(dma_buf
);
174 if (test_data
->dma_buf
)
175 dma_buf_put(test_data
->dma_buf
);
176 test_data
->dma_buf
= dma_buf
;
179 case ION_IOC_TEST_DMA_MAPPING
:
181 ret
= ion_handle_test_dma(test_data
->dev
, test_data
->dma_buf
,
182 u64_to_uptr(data
.test_rw
.ptr
),
183 data
.test_rw
.offset
, data
.test_rw
.size
,
187 case ION_IOC_TEST_KERNEL_MAPPING
:
189 ret
= ion_handle_test_kernel(test_data
->dma_buf
,
190 u64_to_uptr(data
.test_rw
.ptr
),
191 data
.test_rw
.offset
, data
.test_rw
.size
,
199 if (_IOC_DIR(cmd
) & _IOC_READ
) {
200 if (copy_to_user((void __user
*)arg
, &data
, sizeof(data
)))
206 static int ion_test_open(struct inode
*inode
, struct file
*file
)
208 struct ion_test_data
*data
;
209 struct miscdevice
*miscdev
= file
->private_data
;
211 data
= kzalloc(sizeof(struct ion_test_data
), GFP_KERNEL
);
215 data
->dev
= miscdev
->parent
;
217 file
->private_data
= data
;
222 static int ion_test_release(struct inode
*inode
, struct file
*file
)
224 struct ion_test_data
*data
= file
->private_data
;
231 static const struct file_operations ion_test_fops
= {
232 .owner
= THIS_MODULE
,
233 .unlocked_ioctl
= ion_test_ioctl
,
234 .compat_ioctl
= ion_test_ioctl
,
235 .open
= ion_test_open
,
236 .release
= ion_test_release
,
239 static int __init
ion_test_probe(struct platform_device
*pdev
)
242 struct ion_test_device
*testdev
;
244 testdev
= devm_kzalloc(&pdev
->dev
, sizeof(struct ion_test_device
),
249 testdev
->misc
.minor
= MISC_DYNAMIC_MINOR
;
250 testdev
->misc
.name
= "ion-test";
251 testdev
->misc
.fops
= &ion_test_fops
;
252 testdev
->misc
.parent
= &pdev
->dev
;
253 ret
= misc_register(&testdev
->misc
);
255 pr_err("failed to register misc device.\n");
259 platform_set_drvdata(pdev
, testdev
);
264 static int ion_test_remove(struct platform_device
*pdev
)
266 struct ion_test_device
*testdev
;
268 testdev
= platform_get_drvdata(pdev
);
272 misc_deregister(&testdev
->misc
);
276 static struct platform_device
*ion_test_pdev
;
277 static struct platform_driver ion_test_platform_driver
= {
278 .remove
= ion_test_remove
,
284 static int __init
ion_test_init(void)
286 ion_test_pdev
= platform_device_register_simple("ion-test",
291 return platform_driver_probe(&ion_test_platform_driver
, ion_test_probe
);
294 static void __exit
ion_test_exit(void)
296 platform_driver_unregister(&ion_test_platform_driver
);
297 platform_device_unregister(ion_test_pdev
);
300 module_init(ion_test_init
);
301 module_exit(ion_test_exit
);
302 MODULE_LICENSE("GPL v2");