aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/tegra/avp/trpc_sema.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/tegra/avp/trpc_sema.c')
-rw-r--r--drivers/media/video/tegra/avp/trpc_sema.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/avp/trpc_sema.c b/drivers/media/video/tegra/avp/trpc_sema.c
new file mode 100644
index 00000000000..cd717a1a0ca
--- /dev/null
+++ b/drivers/media/video/tegra/avp/trpc_sema.c
@@ -0,0 +1,244 @@
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
32struct tegra_sema_info {
33 struct file *file;
34 wait_queue_head_t wq;
35 spinlock_t lock;
36 int count;
37};
38
39static int rpc_sema_minor = -1;
40
41static inline bool is_trpc_sema_file(struct file *file)
42{
43 dev_t rdev = file->f_dentry->d_inode->i_rdev;
44
45 if (MAJOR(rdev) == MISC_MAJOR && MINOR(rdev) == rpc_sema_minor)
46 return true;
47 return false;
48}
49
50struct tegra_sema_info *trpc_sema_get_from_fd(int fd)
51{
52 struct file *file;
53
54 file = fget(fd);
55 if (unlikely(file == NULL)) {
56 pr_err("%s: fd %d is invalid\n", __func__, fd);
57 return ERR_PTR(-EINVAL);
58 }
59
60 if (!is_trpc_sema_file(file)) {
61 pr_err("%s: fd (%d) is not a trpc_sema file\n", __func__, fd);
62 fput(file);
63 return ERR_PTR(-EINVAL);
64 }
65
66 return file->private_data;
67}
68
69void trpc_sema_put(struct tegra_sema_info *info)
70{
71 if (info->file)
72 fput(info->file);
73}
74
75int tegra_sema_signal(struct tegra_sema_info *info)
76{
77 unsigned long flags;
78
79 if (!info)
80 return -EINVAL;
81
82 spin_lock_irqsave(&info->lock, flags);
83 info->count++;
84 wake_up_interruptible_all(&info->wq);
85 spin_unlock_irqrestore(&info->lock, flags);
86 return 0;
87}
88
89int tegra_sema_wait(struct tegra_sema_info *info, long *timeout)
90{
91 unsigned long flags;
92 int ret = 0;
93 unsigned long endtime;
94 long timeleft = *timeout;
95
96 *timeout = 0;
97 if (timeleft < 0)
98 timeleft = MAX_SCHEDULE_TIMEOUT;
99
100 timeleft = msecs_to_jiffies(timeleft);
101 endtime = jiffies + timeleft;
102
103again:
104 if (timeleft)
105 ret = wait_event_interruptible_timeout(info->wq,
106 info->count > 0,
107 timeleft);
108 spin_lock_irqsave(&info->lock, flags);
109 if (info->count > 0) {
110 info->count--;
111 ret = 0;
112 } else if (ret == 0 || timeout == 0) {
113 ret = -ETIMEDOUT;
114 } else if (ret < 0) {
115 ret = -EINTR;
116 if (timeleft != MAX_SCHEDULE_TIMEOUT &&
117 time_before(jiffies, endtime))
118 *timeout = jiffies_to_msecs(endtime - jiffies);
119 else
120 *timeout = 0;
121 } else {
122 /* we woke up but someone else got the semaphore and we have
123 * time left, try again */
124 timeleft = ret;
125 spin_unlock_irqrestore(&info->lock, flags);
126 goto again;
127 }
128 spin_unlock_irqrestore(&info->lock, flags);
129 return ret;
130}
131
132int tegra_sema_open(struct tegra_sema_info **sema)
133{
134 struct tegra_sema_info *info;
135 info = kzalloc(sizeof(struct tegra_sema_info), GFP_KERNEL);
136 if (!info)
137 return -ENOMEM;
138
139 init_waitqueue_head(&info->wq);
140 spin_lock_init(&info->lock);
141 *sema = info;
142 return 0;
143}
144
145static int trpc_sema_open(struct inode *inode, struct file *file)
146{
147 struct tegra_sema_info *info;
148 int ret;
149
150 ret = tegra_sema_open(&info);
151 if (ret < 0)
152 return ret;
153
154 info->file = file;
155 nonseekable_open(inode, file);
156 file->private_data = info;
157 return 0;
158}
159
160int tegra_sema_release(struct tegra_sema_info *sema)
161{
162 kfree(sema);
163 return 0;
164}
165
166static int trpc_sema_release(struct inode *inode, struct file *file)
167{
168 struct tegra_sema_info *info = file->private_data;
169
170 file->private_data = NULL;
171 tegra_sema_release(info);
172 return 0;
173}
174
175static long trpc_sema_ioctl(struct file *file, unsigned int cmd,
176 unsigned long arg)
177{
178 struct tegra_sema_info *info = file->private_data;
179 int ret;
180 long timeout;
181
182 if (_IOC_TYPE(cmd) != TEGRA_SEMA_IOCTL_MAGIC ||
183 _IOC_NR(cmd) < TEGRA_SEMA_IOCTL_MIN_NR ||
184 _IOC_NR(cmd) > TEGRA_SEMA_IOCTL_MAX_NR)
185 return -ENOTTY;
186 else if (!info)
187 return -EINVAL;
188
189 switch (cmd) {
190 case TEGRA_SEMA_IOCTL_WAIT:
191 if (copy_from_user(&timeout, (void __user *)arg, sizeof(long)))
192 return -EFAULT;
193 ret = tegra_sema_wait(info, &timeout);
194 if (ret != -EINTR)
195 break;
196 if (copy_to_user((void __user *)arg, &timeout, sizeof(long)))
197 ret = -EFAULT;
198 break;
199 case TEGRA_SEMA_IOCTL_SIGNAL:
200 ret = tegra_sema_signal(info);
201 break;
202 default:
203 pr_err("%s: Unknown tegra_sema ioctl 0x%x\n", __func__,
204 _IOC_NR(cmd));
205 ret = -ENOTTY;
206 break;
207 }
208 return ret;
209}
210
211static const struct file_operations trpc_sema_misc_fops = {
212 .owner = THIS_MODULE,
213 .open = trpc_sema_open,
214 .release = trpc_sema_release,
215 .unlocked_ioctl = trpc_sema_ioctl,
216};
217
218static struct miscdevice trpc_sema_misc_device = {
219 .minor = MISC_DYNAMIC_MINOR,
220 .name = "tegra_sema",
221 .fops = &trpc_sema_misc_fops,
222};
223
224int __init trpc_sema_init(void)
225{
226 int ret;
227
228 if (rpc_sema_minor >= 0) {
229 pr_err("%s: trpc_sema already registered\n", __func__);
230 return -EBUSY;
231 }
232
233 ret = misc_register(&trpc_sema_misc_device);
234 if (ret) {
235 pr_err("%s: can't register misc device\n", __func__);
236 return ret;
237 }
238
239 rpc_sema_minor = trpc_sema_misc_device.minor;
240 pr_info("%s: registered misc dev %d:%d\n", __func__, MISC_MAJOR,
241 rpc_sema_minor);
242
243 return 0;
244}