Branch data Line data Source code
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/export.h>
22 : : #include <linux/file.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 : 0 : static struct posix_clock *get_posix_clock(struct file *fp)
34 : : {
35 : 0 : struct posix_clock *clk = fp->private_data;
36 : :
37 : 0 : down_read(&clk->rwsem);
38 : :
39 [ # # ]: 0 : if (!clk->zombie)
40 : : return clk;
41 : :
42 : 0 : up_read(&clk->rwsem);
43 : :
44 : : return NULL;
45 : : }
46 : :
47 : : static void put_posix_clock(struct posix_clock *clk)
48 : : {
49 : 0 : up_read(&clk->rwsem);
50 : : }
51 : :
52 : 0 : static ssize_t posix_clock_read(struct file *fp, char __user *buf,
53 : : size_t count, loff_t *ppos)
54 : : {
55 : 0 : struct posix_clock *clk = get_posix_clock(fp);
56 : : int err = -EINVAL;
57 : :
58 [ # # ]: 0 : if (!clk)
59 : : return -ENODEV;
60 : :
61 [ # # ]: 0 : if (clk->ops.read)
62 : 0 : err = clk->ops.read(clk, fp->f_flags, buf, count);
63 : :
64 : : put_posix_clock(clk);
65 : :
66 : 0 : return err;
67 : : }
68 : :
69 : 0 : static unsigned int posix_clock_poll(struct file *fp, poll_table *wait)
70 : : {
71 : 0 : struct posix_clock *clk = get_posix_clock(fp);
72 : : int result = 0;
73 : :
74 [ # # ]: 0 : if (!clk)
75 : : return -ENODEV;
76 : :
77 [ # # ]: 0 : if (clk->ops.poll)
78 : 0 : result = clk->ops.poll(clk, fp, wait);
79 : :
80 : : put_posix_clock(clk);
81 : :
82 : 0 : return result;
83 : : }
84 : :
85 : 0 : static int posix_clock_fasync(int fd, struct file *fp, int on)
86 : : {
87 : 0 : struct posix_clock *clk = get_posix_clock(fp);
88 : : int err = 0;
89 : :
90 [ # # ]: 0 : if (!clk)
91 : : return -ENODEV;
92 : :
93 [ # # ]: 0 : if (clk->ops.fasync)
94 : 0 : err = clk->ops.fasync(clk, fd, fp, on);
95 : :
96 : : put_posix_clock(clk);
97 : :
98 : 0 : return err;
99 : : }
100 : :
101 : 0 : static int posix_clock_mmap(struct file *fp, struct vm_area_struct *vma)
102 : : {
103 : 0 : struct posix_clock *clk = get_posix_clock(fp);
104 : : int err = -ENODEV;
105 : :
106 [ # # ]: 0 : if (!clk)
107 : : return -ENODEV;
108 : :
109 [ # # ]: 0 : if (clk->ops.mmap)
110 : 0 : err = clk->ops.mmap(clk, vma);
111 : :
112 : : put_posix_clock(clk);
113 : :
114 : 0 : return err;
115 : : }
116 : :
117 : 0 : static long posix_clock_ioctl(struct file *fp,
118 : : unsigned int cmd, unsigned long arg)
119 : : {
120 : 0 : struct posix_clock *clk = get_posix_clock(fp);
121 : : int err = -ENOTTY;
122 : :
123 [ # # ]: 0 : if (!clk)
124 : : return -ENODEV;
125 : :
126 [ # # ]: 0 : if (clk->ops.ioctl)
127 : 0 : err = clk->ops.ioctl(clk, cmd, arg);
128 : :
129 : : put_posix_clock(clk);
130 : :
131 : 0 : 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 : 0 : static int posix_clock_open(struct inode *inode, struct file *fp)
154 : : {
155 : : int err;
156 : 0 : struct posix_clock *clk =
157 : 0 : container_of(inode->i_cdev, struct posix_clock, cdev);
158 : :
159 : 0 : down_read(&clk->rwsem);
160 : :
161 [ # # ]: 0 : if (clk->zombie) {
162 : : err = -ENODEV;
163 : : goto out;
164 : : }
165 [ # # ]: 0 : if (clk->ops.open)
166 : 0 : err = clk->ops.open(clk, fp->f_mode);
167 : : else
168 : : err = 0;
169 : :
170 [ # # ]: 0 : if (!err) {
171 : : kref_get(&clk->kref);
172 : 0 : fp->private_data = clk;
173 : : }
174 : : out:
175 : 0 : up_read(&clk->rwsem);
176 : 0 : return err;
177 : : }
178 : :
179 : 0 : static int posix_clock_release(struct inode *inode, struct file *fp)
180 : : {
181 : 0 : struct posix_clock *clk = fp->private_data;
182 : : int err = 0;
183 : :
184 [ # # ]: 0 : if (clk->ops.release)
185 : 0 : err = clk->ops.release(clk);
186 : :
187 : 0 : kref_put(&clk->kref, delete_clock);
188 : :
189 : 0 : fp->private_data = NULL;
190 : :
191 : 0 : 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 : 0 : int posix_clock_register(struct posix_clock *clk, dev_t devid)
210 : : {
211 : : int err;
212 : :
213 : : kref_init(&clk->kref);
214 : 0 : init_rwsem(&clk->rwsem);
215 : :
216 : 0 : cdev_init(&clk->cdev, &posix_clock_file_operations);
217 : 0 : clk->cdev.owner = clk->ops.owner;
218 : 0 : err = cdev_add(&clk->cdev, devid, 1);
219 : :
220 : 0 : return err;
221 : : }
222 : : EXPORT_SYMBOL_GPL(posix_clock_register);
223 : :
224 : 0 : static void delete_clock(struct kref *kref)
225 : : {
226 : 0 : struct posix_clock *clk = container_of(kref, struct posix_clock, kref);
227 : :
228 [ # # ]: 0 : if (clk->release)
229 : 0 : clk->release(clk);
230 : 0 : }
231 : :
232 : 0 : void posix_clock_unregister(struct posix_clock *clk)
233 : : {
234 : 0 : cdev_del(&clk->cdev);
235 : :
236 : 0 : down_write(&clk->rwsem);
237 : 0 : clk->zombie = true;
238 : 0 : up_write(&clk->rwsem);
239 : :
240 : 0 : kref_put(&clk->kref, delete_clock);
241 : 0 : }
242 : : EXPORT_SYMBOL_GPL(posix_clock_unregister);
243 : :
244 : : struct posix_clock_desc {
245 : : struct file *fp;
246 : : struct posix_clock *clk;
247 : : };
248 : :
249 : 0 : static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
250 : : {
251 : 0 : struct file *fp = fget(CLOCKID_TO_FD(id));
252 : : int err = -EINVAL;
253 : :
254 [ # # ]: 0 : if (!fp)
255 : : return err;
256 : :
257 [ # # ][ # # ]: 0 : if (fp->f_op->open != posix_clock_open || !fp->private_data)
258 : : goto out;
259 : :
260 : 0 : cd->fp = fp;
261 : 0 : cd->clk = get_posix_clock(fp);
262 : :
263 [ # # ]: 0 : err = cd->clk ? 0 : -ENODEV;
264 : : out:
265 [ # # ]: 0 : if (err)
266 : 0 : fput(fp);
267 : 0 : return err;
268 : : }
269 : :
270 : : static void put_clock_desc(struct posix_clock_desc *cd)
271 : : {
272 : 0 : put_posix_clock(cd->clk);
273 : 0 : fput(cd->fp);
274 : : }
275 : :
276 : 0 : static int pc_clock_adjtime(clockid_t id, struct timex *tx)
277 : : {
278 : : struct posix_clock_desc cd;
279 : : int err;
280 : :
281 : 0 : err = get_clock_desc(id, &cd);
282 [ # # ]: 0 : if (err)
283 : : return err;
284 : :
285 [ # # ]: 0 : if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
286 : : err = -EACCES;
287 : : goto out;
288 : : }
289 : :
290 [ # # ]: 0 : if (cd.clk->ops.clock_adjtime)
291 : 0 : err = cd.clk->ops.clock_adjtime(cd.clk, tx);
292 : : else
293 : : err = -EOPNOTSUPP;
294 : : out:
295 : : put_clock_desc(&cd);
296 : :
297 : 0 : return err;
298 : : }
299 : :
300 : 0 : static int pc_clock_gettime(clockid_t id, struct timespec *ts)
301 : : {
302 : : struct posix_clock_desc cd;
303 : : int err;
304 : :
305 : 0 : err = get_clock_desc(id, &cd);
306 [ # # ]: 0 : if (err)
307 : : return err;
308 : :
309 [ # # ]: 0 : if (cd.clk->ops.clock_gettime)
310 : 0 : err = cd.clk->ops.clock_gettime(cd.clk, ts);
311 : : else
312 : : err = -EOPNOTSUPP;
313 : :
314 : : put_clock_desc(&cd);
315 : :
316 : 0 : return err;
317 : : }
318 : :
319 : 0 : static int pc_clock_getres(clockid_t id, struct timespec *ts)
320 : : {
321 : : struct posix_clock_desc cd;
322 : : int err;
323 : :
324 : 0 : err = get_clock_desc(id, &cd);
325 [ # # ]: 0 : if (err)
326 : : return err;
327 : :
328 [ # # ]: 0 : if (cd.clk->ops.clock_getres)
329 : 0 : err = cd.clk->ops.clock_getres(cd.clk, ts);
330 : : else
331 : : err = -EOPNOTSUPP;
332 : :
333 : : put_clock_desc(&cd);
334 : :
335 : 0 : return err;
336 : : }
337 : :
338 : 0 : static int pc_clock_settime(clockid_t id, const struct timespec *ts)
339 : : {
340 : : struct posix_clock_desc cd;
341 : : int err;
342 : :
343 : 0 : err = get_clock_desc(id, &cd);
344 [ # # ]: 0 : if (err)
345 : : return err;
346 : :
347 [ # # ]: 0 : if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
348 : : err = -EACCES;
349 : : goto out;
350 : : }
351 : :
352 [ # # ]: 0 : if (cd.clk->ops.clock_settime)
353 : 0 : err = cd.clk->ops.clock_settime(cd.clk, ts);
354 : : else
355 : : err = -EOPNOTSUPP;
356 : : out:
357 : : put_clock_desc(&cd);
358 : :
359 : 0 : return err;
360 : : }
361 : :
362 : 0 : static int pc_timer_create(struct k_itimer *kit)
363 : : {
364 : 0 : clockid_t id = kit->it_clock;
365 : : struct posix_clock_desc cd;
366 : : int err;
367 : :
368 : 0 : err = get_clock_desc(id, &cd);
369 [ # # ]: 0 : if (err)
370 : : return err;
371 : :
372 [ # # ]: 0 : if (cd.clk->ops.timer_create)
373 : 0 : err = cd.clk->ops.timer_create(cd.clk, kit);
374 : : else
375 : : err = -EOPNOTSUPP;
376 : :
377 : : put_clock_desc(&cd);
378 : :
379 : 0 : return err;
380 : : }
381 : :
382 : 0 : static int pc_timer_delete(struct k_itimer *kit)
383 : : {
384 : 0 : clockid_t id = kit->it_clock;
385 : : struct posix_clock_desc cd;
386 : : int err;
387 : :
388 : 0 : err = get_clock_desc(id, &cd);
389 [ # # ]: 0 : if (err)
390 : : return err;
391 : :
392 [ # # ]: 0 : if (cd.clk->ops.timer_delete)
393 : 0 : err = cd.clk->ops.timer_delete(cd.clk, kit);
394 : : else
395 : : err = -EOPNOTSUPP;
396 : :
397 : : put_clock_desc(&cd);
398 : :
399 : 0 : return err;
400 : : }
401 : :
402 : 0 : static void pc_timer_gettime(struct k_itimer *kit, struct itimerspec *ts)
403 : : {
404 : 0 : clockid_t id = kit->it_clock;
405 : : struct posix_clock_desc cd;
406 : :
407 [ # # ]: 0 : if (get_clock_desc(id, &cd))
408 : 0 : return;
409 : :
410 [ # # ]: 0 : if (cd.clk->ops.timer_gettime)
411 : 0 : cd.clk->ops.timer_gettime(cd.clk, kit, ts);
412 : :
413 : : put_clock_desc(&cd);
414 : : }
415 : :
416 : 0 : static int pc_timer_settime(struct k_itimer *kit, int flags,
417 : : struct itimerspec *ts, struct itimerspec *old)
418 : : {
419 : 0 : clockid_t id = kit->it_clock;
420 : : struct posix_clock_desc cd;
421 : : int err;
422 : :
423 : 0 : err = get_clock_desc(id, &cd);
424 [ # # ]: 0 : if (err)
425 : : return err;
426 : :
427 [ # # ]: 0 : if (cd.clk->ops.timer_settime)
428 : 0 : err = cd.clk->ops.timer_settime(cd.clk, kit, flags, ts, old);
429 : : else
430 : : err = -EOPNOTSUPP;
431 : :
432 : : put_clock_desc(&cd);
433 : :
434 : 0 : return err;
435 : : }
436 : :
437 : : struct k_clock clock_posix_dynamic = {
438 : : .clock_getres = pc_clock_getres,
439 : : .clock_set = pc_clock_settime,
440 : : .clock_get = pc_clock_gettime,
441 : : .clock_adj = pc_clock_adjtime,
442 : : .timer_create = pc_timer_create,
443 : : .timer_set = pc_timer_settime,
444 : : .timer_del = pc_timer_delete,
445 : : .timer_get = pc_timer_gettime,
446 : : };
|