temp revert rk change
[firefly-linux-kernel-4.4.55.git] / drivers / media / video / tegra / avp / trpc_sema.c
1 /*
2  * Copyright (C) 2010 Google, Inc.
3  *
4  * Author:
5  *   Dima Zavin <dima@android.com>
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 #include <linux/err.h>
19 #include <linux/file.h>
20 #include <linux/fs.h>
21 #include <linux/miscdevice.h>
22 #include <linux/sched.h>
23 #include <linux/slab.h>
24 #include <linux/spinlock.h>
25 #include <linux/tegra_sema.h>
26 #include <linux/types.h>
27 #include <linux/uaccess.h>
28 #include <linux/wait.h>
29
30 #include "trpc_sema.h"
31
32 struct trpc_sema {
33         wait_queue_head_t       wq;
34         spinlock_t              lock;
35         int                     count;
36 };
37
38 static int rpc_sema_minor = -1;
39
40 static inline bool is_trpc_sema_file(struct file *file)
41 {
42         dev_t rdev = file->f_dentry->d_inode->i_rdev;
43
44         if (MAJOR(rdev) == MISC_MAJOR && MINOR(rdev) == rpc_sema_minor)
45                 return true;
46         return false;
47 }
48
49 struct file *trpc_sema_get_from_fd(int fd)
50 {
51         struct file *file;
52
53         file = fget(fd);
54         if (unlikely(file == NULL)) {
55                 pr_err("%s: fd %d is invalid\n", __func__, fd);
56                 return ERR_PTR(-EINVAL);
57         }
58
59         if (!is_trpc_sema_file(file)) {
60                 pr_err("%s: fd (%d) is not a trpc_sema file\n", __func__, fd);
61                 fput(file);
62                 return ERR_PTR(-EINVAL);
63         }
64
65         return file;
66 }
67
68 int trpc_sema_signal(struct file *file)
69 {
70         struct trpc_sema *info = file->private_data;
71         unsigned long flags;
72
73         if (!info)
74                 return -EINVAL;
75
76         spin_lock_irqsave(&info->lock, flags);
77         info->count++;
78         wake_up_interruptible_all(&info->wq);
79         spin_unlock_irqrestore(&info->lock, flags);
80         return 0;
81 }
82
83 static int trpc_sema_wait(struct trpc_sema *info, long *timeleft)
84 {
85         unsigned long flags;
86         int ret = 0;
87         unsigned long endtime;
88         long timeout = *timeleft;
89
90         *timeleft = 0;
91         if (timeout < 0) {
92                 timeout = MAX_SCHEDULE_TIMEOUT;
93         } else if (timeout > 0) {
94                 timeout = msecs_to_jiffies(timeout);
95                 endtime = jiffies + timeout;
96         }
97
98 again:
99         if (timeout)
100                 ret = wait_event_interruptible_timeout(info->wq,
101                                                        info->count > 0,
102                                                        timeout);
103         spin_lock_irqsave(&info->lock, flags);
104         if (info->count > 0) {
105                 info->count--;
106                 ret = 0;
107         } else if (ret == 0 || timeout == 0) {
108                 ret = -ETIMEDOUT;
109         } else if (ret < 0) {
110                 ret = -EINTR;
111                 if (timeout != MAX_SCHEDULE_TIMEOUT &&
112                     time_before(jiffies, endtime))
113                         *timeleft = jiffies_to_msecs(endtime - jiffies);
114                 else
115                         *timeleft = 0;
116         } else {
117                 /* we woke up but someone else got the semaphore and we have
118                  * time left, try again */
119                 timeout = ret;
120                 spin_unlock_irqrestore(&info->lock, flags);
121                 goto again;
122         }
123         spin_unlock_irqrestore(&info->lock, flags);
124         return ret;
125 }
126
127 static int trpc_sema_open(struct inode *inode, struct file *file)
128 {
129         struct trpc_sema *info;
130
131         info = kzalloc(sizeof(struct trpc_sema), GFP_KERNEL);
132         if (!info)
133                 return -ENOMEM;
134
135         nonseekable_open(inode, file);
136         init_waitqueue_head(&info->wq);
137         spin_lock_init(&info->lock);
138         file->private_data = info;
139         return 0;
140 }
141
142 static int trpc_sema_release(struct inode *inode, struct file *file)
143 {
144         struct trpc_sema *info = file->private_data;
145
146         file->private_data = NULL;
147         kfree(info);
148         return 0;
149 }
150
151 static long trpc_sema_ioctl(struct file *file, unsigned int cmd,
152                             unsigned long arg)
153 {
154         struct trpc_sema *info = file->private_data;
155         int ret;
156         long timeout;
157
158         if (_IOC_TYPE(cmd) != TEGRA_SEMA_IOCTL_MAGIC ||
159             _IOC_NR(cmd) < TEGRA_SEMA_IOCTL_MIN_NR ||
160             _IOC_NR(cmd) > TEGRA_SEMA_IOCTL_MAX_NR)
161                 return -ENOTTY;
162         else if (!info)
163                 return -EINVAL;
164
165         switch (cmd) {
166         case TEGRA_SEMA_IOCTL_WAIT:
167                 if (copy_from_user(&timeout, (void __user *)arg, sizeof(long)))
168                         return -EFAULT;
169                 ret = trpc_sema_wait(info, &timeout);
170                 if (ret != -EINTR)
171                         break;
172                 if (copy_to_user((void __user *)arg, &timeout, sizeof(long)))
173                         ret = -EFAULT;
174                 break;
175         case TEGRA_SEMA_IOCTL_SIGNAL:
176                 ret = trpc_sema_signal(file);
177                 break;
178         default:
179                 pr_err("%s: Unknown tegra_sema ioctl 0x%x\n", __func__,
180                        _IOC_NR(cmd));
181                 ret = -ENOTTY;
182                 break;
183         }
184         return ret;
185 }
186
187 static const struct file_operations trpc_sema_misc_fops = {
188         .owner          = THIS_MODULE,
189         .open           = trpc_sema_open,
190         .release        = trpc_sema_release,
191         .unlocked_ioctl = trpc_sema_ioctl,
192 };
193
194 static struct miscdevice trpc_sema_misc_device = {
195         .minor  = MISC_DYNAMIC_MINOR,
196         .name   = "tegra_sema",
197         .fops   = &trpc_sema_misc_fops,
198 };
199
200 int __init trpc_sema_init(void)
201 {
202         int ret;
203
204         if (rpc_sema_minor >= 0) {
205                 pr_err("%s: trpc_sema already registered\n", __func__);
206                 return -EBUSY;
207         }
208
209         ret = misc_register(&trpc_sema_misc_device);
210         if (ret) {
211                 pr_err("%s: can't register misc device\n", __func__);
212                 return ret;
213         }
214
215         rpc_sema_minor = trpc_sema_misc_device.minor;
216         pr_info("%s: registered misc dev %d:%d\n", __func__, MISC_MAJOR,
217                 rpc_sema_minor);
218
219         return 0;
220 }