blob: 14d3f39219b6bf1df61b65d5c8150beab3ce7166 [file] [log] [blame]
Erik Gilling560b5462012-04-18 13:43:22 -07001/*
2 * drivers/base/sw_sync.c
3 *
4 * Copyright (C) 2012 Google, Inc.
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/kernel.h>
Erik Gilling49795fb2012-05-16 13:14:43 -070018#include <linux/export.h>
Erik Gilling560b5462012-04-18 13:43:22 -070019#include <linux/file.h>
20#include <linux/fs.h>
21#include <linux/miscdevice.h>
22#include <linux/module.h>
23#include <linux/sw_sync.h>
24#include <linux/syscalls.h>
25#include <linux/uaccess.h>
26
27static int sw_sync_cmp(u32 a, u32 b)
28{
29 if (a == b)
30 return 0;
31
32 return ((s32)a - (s32)b) < 0 ? -1 : 1;
33}
34
35struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value)
36{
37 struct sw_sync_pt *pt;
38
39 pt = (struct sw_sync_pt *)
40 sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt));
41
42 pt->value = value;
43
44 return (struct sync_pt *)pt;
45}
Erik Gilling49795fb2012-05-16 13:14:43 -070046EXPORT_SYMBOL(sw_sync_pt_create);
Erik Gilling560b5462012-04-18 13:43:22 -070047
48static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt)
49{
50 struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt;
51 struct sw_sync_timeline *obj =
52 (struct sw_sync_timeline *)sync_pt->parent;
53
54 return (struct sync_pt *) sw_sync_pt_create(obj, pt->value);
55}
56
57static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt)
58{
59 struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
60 struct sw_sync_timeline *obj =
61 (struct sw_sync_timeline *)sync_pt->parent;
62
63 return sw_sync_cmp(obj->value, pt->value) >= 0;
64}
65
66static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b)
67{
68 struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a;
69 struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b;
70
71 return sw_sync_cmp(pt_a->value, pt_b->value);
72}
73
Erik Gilling484101e2012-03-15 17:46:07 -070074static int sw_sync_fill_driver_data(struct sync_pt *sync_pt,
75 void *data, int size)
76{
77 struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
78
79 if (size < sizeof(pt->value))
80 return -ENOMEM;
81
82 memcpy(data, &pt->value, sizeof(pt->value));
83
84 return sizeof(pt->value);
85}
86
Ajay Dudani13ff1342012-10-16 15:18:23 -070087static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline,
88 char *str, int size)
89{
90 struct sw_sync_timeline *timeline =
91 (struct sw_sync_timeline *)sync_timeline;
92 snprintf(str, size, "%d", timeline->value);
93}
94
95static void sw_sync_pt_value_str(struct sync_pt *sync_pt,
96 char *str, int size)
97{
98 struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
99 snprintf(str, size, "%d", pt->value);
100}
101
Erik Gilling560b5462012-04-18 13:43:22 -0700102struct sync_timeline_ops sw_sync_timeline_ops = {
103 .driver_name = "sw_sync",
104 .dup = sw_sync_pt_dup,
105 .has_signaled = sw_sync_pt_has_signaled,
106 .compare = sw_sync_pt_compare,
Erik Gilling484101e2012-03-15 17:46:07 -0700107 .fill_driver_data = sw_sync_fill_driver_data,
Ajay Dudani13ff1342012-10-16 15:18:23 -0700108 .timeline_value_str = sw_sync_timeline_value_str,
109 .pt_value_str = sw_sync_pt_value_str,
Erik Gilling560b5462012-04-18 13:43:22 -0700110};
111
112
113struct sw_sync_timeline *sw_sync_timeline_create(const char *name)
114{
115 struct sw_sync_timeline *obj = (struct sw_sync_timeline *)
116 sync_timeline_create(&sw_sync_timeline_ops,
117 sizeof(struct sw_sync_timeline),
118 name);
119
120 return obj;
121}
Erik Gilling49795fb2012-05-16 13:14:43 -0700122EXPORT_SYMBOL(sw_sync_timeline_create);
Erik Gilling560b5462012-04-18 13:43:22 -0700123
124void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc)
125{
126 obj->value += inc;
127
128 sync_timeline_signal(&obj->obj);
129}
Erik Gilling49795fb2012-05-16 13:14:43 -0700130EXPORT_SYMBOL(sw_sync_timeline_inc);
Erik Gilling560b5462012-04-18 13:43:22 -0700131
132#ifdef CONFIG_SW_SYNC_USER
133/* *WARNING*
134 *
135 * improper use of this can result in deadlocking kernel drivers from userspace.
136 */
137
138/* opening sw_sync create a new sync obj */
139int sw_sync_open(struct inode *inode, struct file *file)
140{
141 struct sw_sync_timeline *obj;
142 char task_comm[TASK_COMM_LEN];
143
144 get_task_comm(task_comm, current);
145
146 obj = sw_sync_timeline_create(task_comm);
147 if (obj == NULL)
148 return -ENOMEM;
149
150 file->private_data = obj;
151
152 return 0;
153}
154
155int sw_sync_release(struct inode *inode, struct file *file)
156{
157 struct sw_sync_timeline *obj = file->private_data;
158 sync_timeline_destroy(&obj->obj);
159 return 0;
160}
161
162long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg)
163{
164 int fd = get_unused_fd();
165 int err;
166 struct sync_pt *pt;
167 struct sync_fence *fence;
168 struct sw_sync_create_fence_data data;
169
170 if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
171 return -EFAULT;
172
173 pt = sw_sync_pt_create(obj, data.value);
174 if (pt == NULL) {
175 err = -ENOMEM;
176 goto err;
177 }
178
179 data.name[sizeof(data.name) - 1] = '\0';
180 fence = sync_fence_create(data.name, pt);
181 if (fence == NULL) {
182 sync_pt_free(pt);
183 err = -ENOMEM;
184 goto err;
185 }
186
187 data.fence = fd;
188 if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
189 sync_fence_put(fence);
190 err = -EFAULT;
191 goto err;
192 }
193
194 sync_fence_install(fence, fd);
195
196 return 0;
197
198err:
199 put_unused_fd(fd);
200 return err;
201}
202
203long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg)
204{
205 u32 value;
206
207 if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
208 return -EFAULT;
209
210 sw_sync_timeline_inc(obj, value);
211
212 return 0;
213}
214
215long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
216{
217 struct sw_sync_timeline *obj = file->private_data;
218
219 switch (cmd) {
220 case SW_SYNC_IOC_CREATE_FENCE:
221 return sw_sync_ioctl_create_fence(obj, arg);
222
223 case SW_SYNC_IOC_INC:
224 return sw_sync_ioctl_inc(obj, arg);
225
226 default:
227 return -ENOTTY;
228 }
229}
230
231static const struct file_operations sw_sync_fops = {
232 .owner = THIS_MODULE,
233 .open = sw_sync_open,
234 .release = sw_sync_release,
235 .unlocked_ioctl = sw_sync_ioctl,
236};
237
238static struct miscdevice sw_sync_dev = {
239 .minor = MISC_DYNAMIC_MINOR,
240 .name = "sw_sync",
241 .fops = &sw_sync_fops,
242};
243
244int __init sw_sync_device_init(void)
245{
246 return misc_register(&sw_sync_dev);
247}
248
249void __exit sw_sync_device_remove(void)
250{
251 misc_deregister(&sw_sync_dev);
252}
253
254module_init(sw_sync_device_init);
255module_exit(sw_sync_device_remove);
256
257#endif /* CONFIG_SW_SYNC_USER */