Merge remote branch 'common/android-2.6.36' into android-tegra-2.6.36
[firefly-linux-kernel-4.4.55.git] / drivers / media / video / tegra / avp / trpc_local.c
1 /*
2  * Copyright (C) 2010 Google, Inc.
3  *
4  * Author:
5  *   Dima Zavin <dima@android.com>
6  *
7  * Based on original NVRM code from NVIDIA, and a partial rewrite by
8  *   Gary King <gking@nvidia.com>
9  *
10  * This software is licensed under the terms of the GNU General Public
11  * License version 2, as published by the Free Software Foundation, and
12  * may be copied, distributed, and modified under those terms.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  */
20
21 #include <linux/err.h>
22 #include <linux/file.h>
23 #include <linux/fs.h>
24 #include <linux/list.h>
25 #include <linux/miscdevice.h>
26 #include <linux/sched.h>
27 #include <linux/slab.h>
28 #include <linux/spinlock.h>
29 #include <linux/tegra_rpc.h>
30 #include <linux/types.h>
31 #include <linux/uaccess.h>
32 #include <linux/wait.h>
33
34 #include "trpc.h"
35 #include "trpc_sema.h"
36
37 struct rpc_info {
38         struct trpc_endpoint    *rpc_ep;
39         struct file             *sema_file;
40 };
41
42 /* ports names reserved for system functions, i.e. communicating with the
43  * AVP */
44 static const char reserved_ports[][TEGRA_RPC_MAX_NAME_LEN] = {
45         "RPC_AVP_PORT",
46         "RPC_CPU_PORT",
47 };
48 static int num_reserved_ports = ARRAY_SIZE(reserved_ports);
49
50 static void rpc_notify_recv(struct trpc_endpoint *ep);
51
52 /* TODO: do we need to do anything when port is closed from the other side? */
53 static struct trpc_ep_ops ep_ops = {
54         .notify_recv    = rpc_notify_recv,
55 };
56
57 static struct trpc_node rpc_node = {
58         .name   = "local",
59         .type   = TRPC_NODE_LOCAL,
60 };
61
62 static void rpc_notify_recv(struct trpc_endpoint *ep)
63 {
64         struct rpc_info *info = trpc_priv(ep);
65
66         if (WARN_ON(!info))
67                 return;
68         if (info->sema_file)
69                 trpc_sema_signal(info->sema_file);
70 }
71
72 static int local_rpc_open(struct inode *inode, struct file *file)
73 {
74         struct rpc_info *info;
75
76         info = kzalloc(sizeof(struct rpc_info), GFP_KERNEL);
77         if (!info)
78                 return -ENOMEM;
79
80         nonseekable_open(inode, file);
81         file->private_data = info;
82         return 0;
83 }
84
85 static int local_rpc_release(struct inode *inode, struct file *file)
86 {
87         struct rpc_info *info = file->private_data;
88
89         if (info->rpc_ep)
90                 trpc_close(info->rpc_ep);
91         if (info->sema_file)
92                 fput(info->sema_file);
93         kfree(info);
94         file->private_data = NULL;
95         return 0;
96 }
97
98 static int __get_port_desc(struct tegra_rpc_port_desc *desc,
99                            unsigned int cmd, unsigned long arg)
100 {
101         unsigned int size = _IOC_SIZE(cmd);
102
103         if (size != sizeof(struct tegra_rpc_port_desc))
104                 return -EINVAL;
105         if (copy_from_user(desc, (void __user *)arg, sizeof(*desc)))
106                 return -EFAULT;
107
108         desc->name[TEGRA_RPC_MAX_NAME_LEN - 1] = '\0';
109         return 0;
110 }
111
112 static char uniq_name[] = "aaaaaaaa+";
113 static const int uniq_len = sizeof(uniq_name) - 1;
114 static DEFINE_MUTEX(uniq_lock);
115
116 static void _gen_port_name(char *new_name)
117 {
118         int i;
119
120         mutex_lock(&uniq_lock);
121         for (i = 0; i < uniq_len - 1; i++) {
122                 ++uniq_name[i];
123                 if (uniq_name[i] != 'z')
124                         break;
125                 uniq_name[i] = 'a';
126         }
127         strlcpy(new_name, uniq_name, TEGRA_RPC_MAX_NAME_LEN);
128         mutex_unlock(&uniq_lock);
129 }
130
131 static int _validate_port_name(const char *name)
132 {
133         int i;
134
135         for (i = 0; i < num_reserved_ports; i++)
136                 if (!strncmp(name, reserved_ports[i], TEGRA_RPC_MAX_NAME_LEN))
137                         return -EINVAL;
138         return 0;
139 }
140
141 static long local_rpc_ioctl(struct file *file, unsigned int cmd,
142                             unsigned long arg)
143 {
144         struct rpc_info *info = file->private_data;
145         struct tegra_rpc_port_desc desc;
146         struct trpc_endpoint *ep;
147         int ret = 0;
148
149         if (_IOC_TYPE(cmd) != TEGRA_RPC_IOCTL_MAGIC ||
150             _IOC_NR(cmd) < TEGRA_RPC_IOCTL_MIN_NR ||
151             _IOC_NR(cmd) > TEGRA_RPC_IOCTL_MAX_NR) {
152                 ret = -ENOTTY;
153                 goto err;
154         }
155
156         switch (cmd) {
157         case TEGRA_RPC_IOCTL_PORT_CREATE:
158                 if (info->rpc_ep) {
159                         ret = -EINVAL;
160                         goto err;
161                 }
162                 ret = __get_port_desc(&desc, cmd, arg);
163                 if (ret)
164                         goto err;
165                 if (desc.name[0]) {
166                         ret = _validate_port_name(desc.name);
167                         if (ret)
168                                 goto err;
169                 } else {
170                         _gen_port_name(desc.name);
171                 }
172                 if (desc.notify_fd != -1) {
173                         /* grab a reference to the trpc_sema fd */
174                         info->sema_file = trpc_sema_get_from_fd(desc.notify_fd);
175                         if (IS_ERR(info->sema_file)) {
176                                 ret = PTR_ERR(info->sema_file);
177                                 info->sema_file = NULL;
178                                 goto err;
179                         }
180                 }
181                 ep = trpc_create(&rpc_node, desc.name, &ep_ops, info);
182                 if (IS_ERR(ep)) {
183                         ret = PTR_ERR(ep);
184                         if (info->sema_file)
185                                 fput(info->sema_file);
186                         info->sema_file = NULL;
187                         goto err;
188                 }
189                 info->rpc_ep = ep;
190                 break;
191         case TEGRA_RPC_IOCTL_PORT_GET_NAME:
192                 if (!info->rpc_ep) {
193                         ret = -EINVAL;
194                         goto err;
195                 }
196                 if (copy_to_user((void __user *)arg,
197                                  trpc_name(info->rpc_ep),
198                                  TEGRA_RPC_MAX_NAME_LEN)) {
199                         ret = -EFAULT;
200                         goto err;
201                 }
202                 break;
203         case TEGRA_RPC_IOCTL_PORT_CONNECT:
204                 if (!info->rpc_ep) {
205                         ret = -EINVAL;
206                         goto err;
207                 }
208                 ret = trpc_connect(info->rpc_ep, (long)arg);
209                 if (ret) {
210                         pr_err("%s: can't connect to '%s' (%d)\n", __func__,
211                                trpc_name(info->rpc_ep), ret);
212                         goto err;
213                 }
214                 break;
215         case TEGRA_RPC_IOCTL_PORT_LISTEN:
216                 if (!info->rpc_ep) {
217                         ret = -EINVAL;
218                         goto err;
219                 }
220                 ret = trpc_wait_peer(info->rpc_ep, (long)arg);
221                 if (ret) {
222                         pr_err("%s: error waiting for peer for '%s' (%d)\n",
223                                __func__, trpc_name(info->rpc_ep), ret);
224                         goto err;
225                 }
226                 break;
227         default:
228                 pr_err("%s: unknown cmd %d\n", __func__, _IOC_NR(cmd));
229                 ret = -EINVAL;
230                 goto err;
231         }
232
233         return 0;
234
235 err:
236         if (ret && ret != -ERESTARTSYS)
237                 pr_err("tegra_rpc: pid=%d ioctl=%x/%lx (%x) ret=%d\n",
238                        current->pid, cmd, arg, _IOC_NR(cmd), ret);
239         return (long)ret;
240 }
241
242 static ssize_t local_rpc_write(struct file *file, const char __user *buf,
243                                size_t count, loff_t *ppos)
244 {
245         struct rpc_info *info = file->private_data;
246         u8 data[TEGRA_RPC_MAX_MSG_LEN];
247         int ret;
248
249         if (!info)
250                 return -EINVAL;
251         else if (count > TEGRA_RPC_MAX_MSG_LEN)
252                 return -EINVAL;
253
254         if (copy_from_user(data, buf, count))
255                 return -EFAULT;
256
257         ret = trpc_send_msg(&rpc_node, info->rpc_ep, data, count,
258                             GFP_KERNEL);
259         if (ret)
260                 return ret;
261         return count;
262 }
263
264 static ssize_t local_rpc_read(struct file *file, char __user *buf, size_t max,
265                               loff_t *ppos)
266 {
267         struct rpc_info *info = file->private_data;
268         int ret;
269         u8 data[TEGRA_RPC_MAX_MSG_LEN];
270
271         if (max > TEGRA_RPC_MAX_MSG_LEN)
272                 return -EINVAL;
273
274         ret = trpc_recv_msg(&rpc_node, info->rpc_ep, data,
275                             TEGRA_RPC_MAX_MSG_LEN, 0);
276         if (ret == 0)
277                 return 0;
278         else if (ret < 0)
279                 return ret;
280         else if (ret > max)
281                 return -ENOSPC;
282         else if (copy_to_user(buf, data, ret))
283                 return -EFAULT;
284
285         return ret;
286 }
287
288 static const struct file_operations local_rpc_misc_fops = {
289         .owner          = THIS_MODULE,
290         .open           = local_rpc_open,
291         .release        = local_rpc_release,
292         .unlocked_ioctl = local_rpc_ioctl,
293         .write          = local_rpc_write,
294         .read           = local_rpc_read,
295 };
296
297 static struct miscdevice local_rpc_misc_device = {
298         .minor  = MISC_DYNAMIC_MINOR,
299         .name   = "tegra_rpc",
300         .fops   = &local_rpc_misc_fops,
301 };
302
303 int __init rpc_local_init(void)
304 {
305         int ret;
306
307         ret = trpc_sema_init();
308         if (ret) {
309                 pr_err("%s: error in trpc_sema_init\n", __func__);
310                 goto err_sema_init;
311         }
312
313         ret = misc_register(&local_rpc_misc_device);
314         if (ret) {
315                 pr_err("%s: can't register misc device\n", __func__);
316                 goto err_misc;
317         }
318
319         ret = trpc_node_register(&rpc_node);
320         if (ret) {
321                 pr_err("%s: can't register rpc node\n", __func__);
322                 goto err_node_reg;
323         }
324         return 0;
325
326 err_node_reg:
327         misc_deregister(&local_rpc_misc_device);
328 err_misc:
329 err_sema_init:
330         return ret;
331 }
332
333 module_init(rpc_local_init);