9ff815ad1cb16fc60db94ecfed6ded7a92f1efde
[linux-2.6-microblaze.git] / drivers / staging / android / ion / ion-ioctl.c
1 /*
2  *
3  * Copyright (C) 2011 Google, Inc.
4  *
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.
8  *
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.
13  *
14  */
15
16 #include <linux/kernel.h>
17 #include <linux/file.h>
18 #include <linux/fs.h>
19 #include <linux/uaccess.h>
20
21 #include "ion.h"
22 #include "ion_priv.h"
23 #include "compat_ion.h"
24
25 union ion_ioctl_arg {
26         struct ion_fd_data fd;
27         struct ion_allocation_data allocation;
28         struct ion_handle_data handle;
29         struct ion_custom_data custom;
30         struct ion_heap_query query;
31 };
32
33 static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
34 {
35         int ret = 0;
36
37         switch (cmd) {
38         case ION_IOC_HEAP_QUERY:
39                 ret = arg->query.reserved0 != 0;
40                 ret |= arg->query.reserved1 != 0;
41                 ret |= arg->query.reserved2 != 0;
42                 break;
43         default:
44                 break;
45         }
46
47         return ret ? -EINVAL : 0;
48 }
49
50 /* fix up the cases where the ioctl direction bits are incorrect */
51 static unsigned int ion_ioctl_dir(unsigned int cmd)
52 {
53         switch (cmd) {
54         case ION_IOC_SYNC:
55         case ION_IOC_FREE:
56         case ION_IOC_CUSTOM:
57                 return _IOC_WRITE;
58         default:
59                 return _IOC_DIR(cmd);
60         }
61 }
62
63 long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
64 {
65         struct ion_client *client = filp->private_data;
66         struct ion_device *dev = client->dev;
67         struct ion_handle *cleanup_handle = NULL;
68         int ret = 0;
69         unsigned int dir;
70         union ion_ioctl_arg data;
71
72         dir = ion_ioctl_dir(cmd);
73
74         if (_IOC_SIZE(cmd) > sizeof(data))
75                 return -EINVAL;
76
77         /*
78          * The copy_from_user is unconditional here for both read and write
79          * to do the validate. If there is no write for the ioctl, the
80          * buffer is cleared
81          */
82         if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
83                 return -EFAULT;
84
85         ret = validate_ioctl_arg(cmd, &data);
86         if (WARN_ON_ONCE(ret))
87                 return ret;
88
89         if (!(dir & _IOC_WRITE))
90                 memset(&data, 0, sizeof(data));
91
92         switch (cmd) {
93         case ION_IOC_ALLOC:
94         {
95                 struct ion_handle *handle;
96
97                 handle = ion_alloc(client, data.allocation.len,
98                                                 data.allocation.align,
99                                                 data.allocation.heap_id_mask,
100                                                 data.allocation.flags);
101                 if (IS_ERR(handle))
102                         return PTR_ERR(handle);
103
104                 data.allocation.handle = handle->id;
105
106                 cleanup_handle = handle;
107                 break;
108         }
109         case ION_IOC_FREE:
110         {
111                 struct ion_handle *handle;
112
113                 mutex_lock(&client->lock);
114                 handle = ion_handle_get_by_id_nolock(client,
115                                                      data.handle.handle);
116                 if (IS_ERR(handle)) {
117                         mutex_unlock(&client->lock);
118                         return PTR_ERR(handle);
119                 }
120                 ion_free_nolock(client, handle);
121                 ion_handle_put_nolock(handle);
122                 mutex_unlock(&client->lock);
123                 break;
124         }
125         case ION_IOC_SHARE:
126         case ION_IOC_MAP:
127         {
128                 struct ion_handle *handle;
129
130                 handle = ion_handle_get_by_id(client, data.handle.handle);
131                 if (IS_ERR(handle))
132                         return PTR_ERR(handle);
133                 data.fd.fd = ion_share_dma_buf_fd(client, handle);
134                 ion_handle_put(handle);
135                 if (data.fd.fd < 0)
136                         ret = data.fd.fd;
137                 break;
138         }
139         case ION_IOC_IMPORT:
140         {
141                 struct ion_handle *handle;
142
143                 handle = ion_import_dma_buf_fd(client, data.fd.fd);
144                 if (IS_ERR(handle))
145                         ret = PTR_ERR(handle);
146                 else
147                         data.handle.handle = handle->id;
148                 break;
149         }
150         case ION_IOC_SYNC:
151         {
152                 ret = ion_sync_for_device(client, data.fd.fd);
153                 break;
154         }
155         case ION_IOC_CUSTOM:
156         {
157                 if (!dev->custom_ioctl)
158                         return -ENOTTY;
159                 ret = dev->custom_ioctl(client, data.custom.cmd,
160                                                 data.custom.arg);
161                 break;
162         }
163         case ION_IOC_HEAP_QUERY:
164                 ret = ion_query_heaps(client, &data.query);
165                 break;
166         default:
167                 return -ENOTTY;
168         }
169
170         if (dir & _IOC_READ) {
171                 if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
172                         if (cleanup_handle)
173                                 ion_free(client, cleanup_handle);
174                         return -EFAULT;
175                 }
176         }
177         return ret;
178 }