2 * Copyright (C) 2010 Google, Inc.
5 * Dima Zavin <dima@android.com>
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.
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.
18 #include <linux/err.h>
19 #include <linux/file.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>
30 #include "trpc_sema.h"
38 static int rpc_sema_minor = -1;
40 static inline bool is_trpc_sema_file(struct file *file)
42 dev_t rdev = file->f_dentry->d_inode->i_rdev;
44 if (MAJOR(rdev) == MISC_MAJOR && MINOR(rdev) == rpc_sema_minor)
49 struct file *trpc_sema_get_from_fd(int fd)
54 if (unlikely(file == NULL)) {
55 pr_err("%s: fd %d is invalid\n", __func__, fd);
56 return ERR_PTR(-EINVAL);
59 if (!is_trpc_sema_file(file)) {
60 pr_err("%s: fd (%d) is not a trpc_sema file\n", __func__, fd);
62 return ERR_PTR(-EINVAL);
68 int trpc_sema_signal(struct file *file)
70 struct trpc_sema *info = file->private_data;
76 spin_lock_irqsave(&info->lock, flags);
78 wake_up_interruptible_all(&info->wq);
79 spin_unlock_irqrestore(&info->lock, flags);
83 static int trpc_sema_wait(struct trpc_sema *info, long *timeleft)
87 unsigned long endtime;
88 long timeout = *timeleft;
92 timeout = MAX_SCHEDULE_TIMEOUT;
93 } else if (timeout > 0) {
94 timeout = msecs_to_jiffies(timeout);
95 endtime = jiffies + timeout;
100 ret = wait_event_interruptible_timeout(info->wq,
103 spin_lock_irqsave(&info->lock, flags);
104 if (info->count > 0) {
107 } else if (ret == 0 || timeout == 0) {
109 } else if (ret < 0) {
111 if (timeout != MAX_SCHEDULE_TIMEOUT &&
112 time_before(jiffies, endtime))
113 *timeleft = jiffies_to_msecs(endtime - jiffies);
117 /* we woke up but someone else got the semaphore and we have
118 * time left, try again */
120 spin_unlock_irqrestore(&info->lock, flags);
123 spin_unlock_irqrestore(&info->lock, flags);
127 static int trpc_sema_open(struct inode *inode, struct file *file)
129 struct trpc_sema *info;
131 info = kzalloc(sizeof(struct trpc_sema), GFP_KERNEL);
135 nonseekable_open(inode, file);
136 init_waitqueue_head(&info->wq);
137 spin_lock_init(&info->lock);
138 file->private_data = info;
142 static int trpc_sema_release(struct inode *inode, struct file *file)
144 struct trpc_sema *info = file->private_data;
146 file->private_data = NULL;
151 static long trpc_sema_ioctl(struct file *file, unsigned int cmd,
154 struct trpc_sema *info = file->private_data;
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)
166 case TEGRA_SEMA_IOCTL_WAIT:
167 if (copy_from_user(&timeout, (void __user *)arg, sizeof(long)))
169 ret = trpc_sema_wait(info, &timeout);
172 if (copy_to_user((void __user *)arg, &timeout, sizeof(long)))
175 case TEGRA_SEMA_IOCTL_SIGNAL:
176 ret = trpc_sema_signal(file);
179 pr_err("%s: Unknown tegra_sema ioctl 0x%x\n", __func__,
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,
194 static struct miscdevice trpc_sema_misc_device = {
195 .minor = MISC_DYNAMIC_MINOR,
196 .name = "tegra_sema",
197 .fops = &trpc_sema_misc_fops,
200 int __init trpc_sema_init(void)
204 if (rpc_sema_minor >= 0) {
205 pr_err("%s: trpc_sema already registered\n", __func__);
209 ret = misc_register(&trpc_sema_misc_device);
211 pr_err("%s: can't register misc device\n", __func__);
215 rpc_sema_minor = trpc_sema_misc_device.minor;
216 pr_info("%s: registered misc dev %d:%d\n", __func__, MISC_MAJOR,