diff options
Diffstat (limited to 'kernel/time/posix-clock.c')
-rw-r--r-- | kernel/time/posix-clock.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c new file mode 100644 index 000000000000..25028dd4fa18 --- /dev/null +++ b/kernel/time/posix-clock.c | |||
@@ -0,0 +1,451 @@ | |||
1 | /* | ||
2 | * posix-clock.c - support for dynamic clock devices | ||
3 | * | ||
4 | * Copyright (C) 2010 OMICRON electronics GmbH | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
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 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/file.h> | ||
22 | #include <linux/mutex.h> | ||
23 | #include <linux/posix-clock.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/syscalls.h> | ||
26 | #include <linux/uaccess.h> | ||
27 | |||
28 | static void delete_clock(struct kref *kref); | ||
29 | |||
30 | /* | ||
31 | * Returns NULL if the posix_clock instance attached to 'fp' is old and stale. | ||
32 | */ | ||
33 | static struct posix_clock *get_posix_clock(struct file *fp) | ||
34 | { | ||
35 | struct posix_clock *clk = fp->private_data; | ||
36 | |||
37 | mutex_lock(&clk->mutex); | ||
38 | |||
39 | if (!clk->zombie) | ||
40 | return clk; | ||
41 | |||
42 | mutex_unlock(&clk->mutex); | ||
43 | |||
44 | return NULL; | ||
45 | } | ||
46 | |||
47 | static void put_posix_clock(struct posix_clock *clk) | ||
48 | { | ||
49 | mutex_unlock(&clk->mutex); | ||
50 | } | ||
51 | |||
52 | static ssize_t posix_clock_read(struct file *fp, char __user *buf, | ||
53 | size_t count, loff_t *ppos) | ||
54 | { | ||
55 | struct posix_clock *clk = get_posix_clock(fp); | ||
56 | int err = -EINVAL; | ||
57 | |||
58 | if (!clk) | ||
59 | return -ENODEV; | ||
60 | |||
61 | if (clk->ops.read) | ||
62 | err = clk->ops.read(clk, fp->f_flags, buf, count); | ||
63 | |||
64 | put_posix_clock(clk); | ||
65 | |||
66 | return err; | ||
67 | } | ||
68 | |||
69 | static unsigned int posix_clock_poll(struct file *fp, poll_table *wait) | ||
70 | { | ||
71 | struct posix_clock *clk = get_posix_clock(fp); | ||
72 | int result = 0; | ||
73 | |||
74 | if (!clk) | ||
75 | return -ENODEV; | ||
76 | |||
77 | if (clk->ops.poll) | ||
78 | result = clk->ops.poll(clk, fp, wait); | ||
79 | |||
80 | put_posix_clock(clk); | ||
81 | |||
82 | return result; | ||
83 | } | ||
84 | |||
85 | static int posix_clock_fasync(int fd, struct file *fp, int on) | ||
86 | { | ||
87 | struct posix_clock *clk = get_posix_clock(fp); | ||
88 | int err = 0; | ||
89 | |||
90 | if (!clk) | ||
91 | return -ENODEV; | ||
92 | |||
93 | if (clk->ops.fasync) | ||
94 | err = clk->ops.fasync(clk, fd, fp, on); | ||
95 | |||
96 | put_posix_clock(clk); | ||
97 | |||
98 | return err; | ||
99 | } | ||
100 | |||
101 | static int posix_clock_mmap(struct file *fp, struct vm_area_struct *vma) | ||
102 | { | ||
103 | struct posix_clock *clk = get_posix_clock(fp); | ||
104 | int err = -ENODEV; | ||
105 | |||
106 | if (!clk) | ||
107 | return -ENODEV; | ||
108 | |||
109 | if (clk->ops.mmap) | ||
110 | err = clk->ops.mmap(clk, vma); | ||
111 | |||
112 | put_posix_clock(clk); | ||
113 | |||
114 | return err; | ||
115 | } | ||
116 | |||
117 | static long posix_clock_ioctl(struct file *fp, | ||
118 | unsigned int cmd, unsigned long arg) | ||
119 | { | ||
120 | struct posix_clock *clk = get_posix_clock(fp); | ||
121 | int err = -ENOTTY; | ||
122 | |||
123 | if (!clk) | ||
124 | return -ENODEV; | ||
125 | |||
126 | if (clk->ops.ioctl) | ||
127 | err = clk->ops.ioctl(clk, cmd, arg); | ||
128 | |||
129 | put_posix_clock(clk); | ||
130 | |||
131 | return err; | ||
132 | } | ||
133 | |||
134 | #ifdef CONFIG_COMPAT | ||
135 | static long posix_clock_compat_ioctl(struct file *fp, | ||
136 | unsigned int cmd, unsigned long arg) | ||
137 | { | ||
138 | struct posix_clock *clk = get_posix_clock(fp); | ||
139 | int err = -ENOTTY; | ||
140 | |||
141 | if (!clk) | ||
142 | return -ENODEV; | ||
143 | |||
144 | if (clk->ops.ioctl) | ||
145 | err = clk->ops.ioctl(clk, cmd, arg); | ||
146 | |||
147 | put_posix_clock(clk); | ||
148 | |||
149 | return err; | ||
150 | } | ||
151 | #endif | ||
152 | |||
153 | static int posix_clock_open(struct inode *inode, struct file *fp) | ||
154 | { | ||
155 | int err; | ||
156 | struct posix_clock *clk = | ||
157 | container_of(inode->i_cdev, struct posix_clock, cdev); | ||
158 | |||
159 | mutex_lock(&clk->mutex); | ||
160 | |||
161 | if (clk->zombie) { | ||
162 | err = -ENODEV; | ||
163 | goto out; | ||
164 | } | ||
165 | if (clk->ops.open) | ||
166 | err = clk->ops.open(clk, fp->f_mode); | ||
167 | else | ||
168 | err = 0; | ||
169 | |||
170 | if (!err) { | ||
171 | kref_get(&clk->kref); | ||
172 | fp->private_data = clk; | ||
173 | } | ||
174 | out: | ||
175 | mutex_unlock(&clk->mutex); | ||
176 | return err; | ||
177 | } | ||
178 | |||
179 | static int posix_clock_release(struct inode *inode, struct file *fp) | ||
180 | { | ||
181 | struct posix_clock *clk = fp->private_data; | ||
182 | int err = 0; | ||
183 | |||
184 | if (clk->ops.release) | ||
185 | err = clk->ops.release(clk); | ||
186 | |||
187 | kref_put(&clk->kref, delete_clock); | ||
188 | |||
189 | fp->private_data = NULL; | ||
190 | |||
191 | return err; | ||
192 | } | ||
193 | |||
194 | static const struct file_operations posix_clock_file_operations = { | ||
195 | .owner = THIS_MODULE, | ||
196 | .llseek = no_llseek, | ||
197 | .read = posix_clock_read, | ||
198 | .poll = posix_clock_poll, | ||
199 | .unlocked_ioctl = posix_clock_ioctl, | ||
200 | .open = posix_clock_open, | ||
201 | .release = posix_clock_release, | ||
202 | .fasync = posix_clock_fasync, | ||
203 | .mmap = posix_clock_mmap, | ||
204 | #ifdef CONFIG_COMPAT | ||
205 | .compat_ioctl = posix_clock_compat_ioctl, | ||
206 | #endif | ||
207 | }; | ||
208 | |||
209 | int posix_clock_register(struct posix_clock *clk, dev_t devid) | ||
210 | { | ||
211 | int err; | ||
212 | |||
213 | kref_init(&clk->kref); | ||
214 | mutex_init(&clk->mutex); | ||
215 | |||
216 | cdev_init(&clk->cdev, &posix_clock_file_operations); | ||
217 | clk->cdev.owner = clk->ops.owner; | ||
218 | err = cdev_add(&clk->cdev, devid, 1); | ||
219 | if (err) | ||
220 | goto no_cdev; | ||
221 | |||
222 | return err; | ||
223 | no_cdev: | ||
224 | mutex_destroy(&clk->mutex); | ||
225 | return err; | ||
226 | } | ||
227 | EXPORT_SYMBOL_GPL(posix_clock_register); | ||
228 | |||
229 | static void delete_clock(struct kref *kref) | ||
230 | { | ||
231 | struct posix_clock *clk = container_of(kref, struct posix_clock, kref); | ||
232 | mutex_destroy(&clk->mutex); | ||
233 | if (clk->release) | ||
234 | clk->release(clk); | ||
235 | } | ||
236 | |||
237 | void posix_clock_unregister(struct posix_clock *clk) | ||
238 | { | ||
239 | cdev_del(&clk->cdev); | ||
240 | |||
241 | mutex_lock(&clk->mutex); | ||
242 | clk->zombie = true; | ||
243 | mutex_unlock(&clk->mutex); | ||
244 | |||
245 | kref_put(&clk->kref, delete_clock); | ||
246 | } | ||
247 | EXPORT_SYMBOL_GPL(posix_clock_unregister); | ||
248 | |||
249 | struct posix_clock_desc { | ||
250 | struct file *fp; | ||
251 | struct posix_clock *clk; | ||
252 | }; | ||
253 | |||
254 | static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd) | ||
255 | { | ||
256 | struct file *fp = fget(CLOCKID_TO_FD(id)); | ||
257 | int err = -EINVAL; | ||
258 | |||
259 | if (!fp) | ||
260 | return err; | ||
261 | |||
262 | if (fp->f_op->open != posix_clock_open || !fp->private_data) | ||
263 | goto out; | ||
264 | |||
265 | cd->fp = fp; | ||
266 | cd->clk = get_posix_clock(fp); | ||
267 | |||
268 | err = cd->clk ? 0 : -ENODEV; | ||
269 | out: | ||
270 | if (err) | ||
271 | fput(fp); | ||
272 | return err; | ||
273 | } | ||
274 | |||
275 | static void put_clock_desc(struct posix_clock_desc *cd) | ||
276 | { | ||
277 | put_posix_clock(cd->clk); | ||
278 | fput(cd->fp); | ||
279 | } | ||
280 | |||
281 | static int pc_clock_adjtime(clockid_t id, struct timex *tx) | ||
282 | { | ||
283 | struct posix_clock_desc cd; | ||
284 | int err; | ||
285 | |||
286 | err = get_clock_desc(id, &cd); | ||
287 | if (err) | ||
288 | return err; | ||
289 | |||
290 | if ((cd.fp->f_mode & FMODE_WRITE) == 0) { | ||
291 | err = -EACCES; | ||
292 | goto out; | ||
293 | } | ||
294 | |||
295 | if (cd.clk->ops.clock_adjtime) | ||
296 | err = cd.clk->ops.clock_adjtime(cd.clk, tx); | ||
297 | else | ||
298 | err = -EOPNOTSUPP; | ||
299 | out: | ||
300 | put_clock_desc(&cd); | ||
301 | |||
302 | return err; | ||
303 | } | ||
304 | |||
305 | static int pc_clock_gettime(clockid_t id, struct timespec *ts) | ||
306 | { | ||
307 | struct posix_clock_desc cd; | ||
308 | int err; | ||
309 | |||
310 | err = get_clock_desc(id, &cd); | ||
311 | if (err) | ||
312 | return err; | ||
313 | |||
314 | if (cd.clk->ops.clock_gettime) | ||
315 | err = cd.clk->ops.clock_gettime(cd.clk, ts); | ||
316 | else | ||
317 | err = -EOPNOTSUPP; | ||
318 | |||
319 | put_clock_desc(&cd); | ||
320 | |||
321 | return err; | ||
322 | } | ||
323 | |||
324 | static int pc_clock_getres(clockid_t id, struct timespec *ts) | ||
325 | { | ||
326 | struct posix_clock_desc cd; | ||
327 | int err; | ||
328 | |||
329 | err = get_clock_desc(id, &cd); | ||
330 | if (err) | ||
331 | return err; | ||
332 | |||
333 | if (cd.clk->ops.clock_getres) | ||
334 | err = cd.clk->ops.clock_getres(cd.clk, ts); | ||
335 | else | ||
336 | err = -EOPNOTSUPP; | ||
337 | |||
338 | put_clock_desc(&cd); | ||
339 | |||
340 | return err; | ||
341 | } | ||
342 | |||
343 | static int pc_clock_settime(clockid_t id, const struct timespec *ts) | ||
344 | { | ||
345 | struct posix_clock_desc cd; | ||
346 | int err; | ||
347 | |||
348 | err = get_clock_desc(id, &cd); | ||
349 | if (err) | ||
350 | return err; | ||
351 | |||
352 | if ((cd.fp->f_mode & FMODE_WRITE) == 0) { | ||
353 | err = -EACCES; | ||
354 | goto out; | ||
355 | } | ||
356 | |||
357 | if (cd.clk->ops.clock_settime) | ||
358 | err = cd.clk->ops.clock_settime(cd.clk, ts); | ||
359 | else | ||
360 | err = -EOPNOTSUPP; | ||
361 | out: | ||
362 | put_clock_desc(&cd); | ||
363 | |||
364 | return err; | ||
365 | } | ||
366 | |||
367 | static int pc_timer_create(struct k_itimer *kit) | ||
368 | { | ||
369 | clockid_t id = kit->it_clock; | ||
370 | struct posix_clock_desc cd; | ||
371 | int err; | ||
372 | |||
373 | err = get_clock_desc(id, &cd); | ||
374 | if (err) | ||
375 | return err; | ||
376 | |||
377 | if (cd.clk->ops.timer_create) | ||
378 | err = cd.clk->ops.timer_create(cd.clk, kit); | ||
379 | else | ||
380 | err = -EOPNOTSUPP; | ||
381 | |||
382 | put_clock_desc(&cd); | ||
383 | |||
384 | return err; | ||
385 | } | ||
386 | |||
387 | static int pc_timer_delete(struct k_itimer *kit) | ||
388 | { | ||
389 | clockid_t id = kit->it_clock; | ||
390 | struct posix_clock_desc cd; | ||
391 | int err; | ||
392 | |||
393 | err = get_clock_desc(id, &cd); | ||
394 | if (err) | ||
395 | return err; | ||
396 | |||
397 | if (cd.clk->ops.timer_delete) | ||
398 | err = cd.clk->ops.timer_delete(cd.clk, kit); | ||
399 | else | ||
400 | err = -EOPNOTSUPP; | ||
401 | |||
402 | put_clock_desc(&cd); | ||
403 | |||
404 | return err; | ||
405 | } | ||
406 | |||
407 | static void pc_timer_gettime(struct k_itimer *kit, struct itimerspec *ts) | ||
408 | { | ||
409 | clockid_t id = kit->it_clock; | ||
410 | struct posix_clock_desc cd; | ||
411 | |||
412 | if (get_clock_desc(id, &cd)) | ||
413 | return; | ||
414 | |||
415 | if (cd.clk->ops.timer_gettime) | ||
416 | cd.clk->ops.timer_gettime(cd.clk, kit, ts); | ||
417 | |||
418 | put_clock_desc(&cd); | ||
419 | } | ||
420 | |||
421 | static int pc_timer_settime(struct k_itimer *kit, int flags, | ||
422 | struct itimerspec *ts, struct itimerspec *old) | ||
423 | { | ||
424 | clockid_t id = kit->it_clock; | ||
425 | struct posix_clock_desc cd; | ||
426 | int err; | ||
427 | |||
428 | err = get_clock_desc(id, &cd); | ||
429 | if (err) | ||
430 | return err; | ||
431 | |||
432 | if (cd.clk->ops.timer_settime) | ||
433 | err = cd.clk->ops.timer_settime(cd.clk, kit, flags, ts, old); | ||
434 | else | ||
435 | err = -EOPNOTSUPP; | ||
436 | |||
437 | put_clock_desc(&cd); | ||
438 | |||
439 | return err; | ||
440 | } | ||
441 | |||
442 | struct k_clock clock_posix_dynamic = { | ||
443 | .clock_getres = pc_clock_getres, | ||
444 | .clock_set = pc_clock_settime, | ||
445 | .clock_get = pc_clock_gettime, | ||
446 | .clock_adj = pc_clock_adjtime, | ||
447 | .timer_create = pc_timer_create, | ||
448 | .timer_set = pc_timer_settime, | ||
449 | .timer_del = pc_timer_delete, | ||
450 | .timer_get = pc_timer_gettime, | ||
451 | }; | ||