Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0+
2 : /*
3 : * Support for dynamic clock devices
4 : *
5 : * Copyright (C) 2010 OMICRON electronics GmbH
6 : */
7 : #include <linux/device.h>
8 : #include <linux/export.h>
9 : #include <linux/file.h>
10 : #include <linux/posix-clock.h>
11 : #include <linux/slab.h>
12 : #include <linux/syscalls.h>
13 : #include <linux/uaccess.h>
14 :
15 : #include "posix-timers.h"
16 :
17 : /*
18 : * Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
19 : */
20 : static struct posix_clock *get_posix_clock(struct file *fp)
21 : {
22 0 : struct posix_clock *clk = fp->private_data;
23 :
24 0 : down_read(&clk->rwsem);
25 :
26 0 : if (!clk->zombie)
27 : return clk;
28 :
29 0 : up_read(&clk->rwsem);
30 :
31 : return NULL;
32 : }
33 :
34 : static void put_posix_clock(struct posix_clock *clk)
35 : {
36 0 : up_read(&clk->rwsem);
37 : }
38 :
39 0 : static ssize_t posix_clock_read(struct file *fp, char __user *buf,
40 : size_t count, loff_t *ppos)
41 : {
42 0 : struct posix_clock *clk = get_posix_clock(fp);
43 0 : int err = -EINVAL;
44 :
45 0 : if (!clk)
46 : return -ENODEV;
47 :
48 0 : if (clk->ops.read)
49 0 : err = clk->ops.read(clk, fp->f_flags, buf, count);
50 :
51 0 : put_posix_clock(clk);
52 :
53 0 : return err;
54 : }
55 :
56 0 : static __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
57 : {
58 0 : struct posix_clock *clk = get_posix_clock(fp);
59 0 : __poll_t result = 0;
60 :
61 0 : if (!clk)
62 : return EPOLLERR;
63 :
64 0 : if (clk->ops.poll)
65 0 : result = clk->ops.poll(clk, fp, wait);
66 :
67 0 : put_posix_clock(clk);
68 :
69 0 : return result;
70 : }
71 :
72 0 : static long posix_clock_ioctl(struct file *fp,
73 : unsigned int cmd, unsigned long arg)
74 : {
75 0 : struct posix_clock *clk = get_posix_clock(fp);
76 0 : int err = -ENOTTY;
77 :
78 0 : if (!clk)
79 : return -ENODEV;
80 :
81 0 : if (clk->ops.ioctl)
82 0 : err = clk->ops.ioctl(clk, cmd, arg);
83 :
84 0 : put_posix_clock(clk);
85 :
86 0 : return err;
87 : }
88 :
89 : #ifdef CONFIG_COMPAT
90 : static long posix_clock_compat_ioctl(struct file *fp,
91 : unsigned int cmd, unsigned long arg)
92 : {
93 : struct posix_clock *clk = get_posix_clock(fp);
94 : int err = -ENOTTY;
95 :
96 : if (!clk)
97 : return -ENODEV;
98 :
99 : if (clk->ops.ioctl)
100 : err = clk->ops.ioctl(clk, cmd, arg);
101 :
102 : put_posix_clock(clk);
103 :
104 : return err;
105 : }
106 : #endif
107 :
108 0 : static int posix_clock_open(struct inode *inode, struct file *fp)
109 : {
110 : int err;
111 0 : struct posix_clock *clk =
112 0 : container_of(inode->i_cdev, struct posix_clock, cdev);
113 :
114 0 : down_read(&clk->rwsem);
115 :
116 0 : if (clk->zombie) {
117 : err = -ENODEV;
118 : goto out;
119 : }
120 0 : if (clk->ops.open)
121 0 : err = clk->ops.open(clk, fp->f_mode);
122 : else
123 : err = 0;
124 :
125 0 : if (!err) {
126 0 : get_device(clk->dev);
127 0 : fp->private_data = clk;
128 : }
129 : out:
130 0 : up_read(&clk->rwsem);
131 0 : return err;
132 : }
133 :
134 0 : static int posix_clock_release(struct inode *inode, struct file *fp)
135 : {
136 0 : struct posix_clock *clk = fp->private_data;
137 0 : int err = 0;
138 :
139 0 : if (clk->ops.release)
140 0 : err = clk->ops.release(clk);
141 :
142 0 : put_device(clk->dev);
143 :
144 0 : fp->private_data = NULL;
145 :
146 0 : return err;
147 : }
148 :
149 : static const struct file_operations posix_clock_file_operations = {
150 : .owner = THIS_MODULE,
151 : .llseek = no_llseek,
152 : .read = posix_clock_read,
153 : .poll = posix_clock_poll,
154 : .unlocked_ioctl = posix_clock_ioctl,
155 : .open = posix_clock_open,
156 : .release = posix_clock_release,
157 : #ifdef CONFIG_COMPAT
158 : .compat_ioctl = posix_clock_compat_ioctl,
159 : #endif
160 : };
161 :
162 0 : int posix_clock_register(struct posix_clock *clk, struct device *dev)
163 : {
164 : int err;
165 :
166 0 : init_rwsem(&clk->rwsem);
167 :
168 0 : cdev_init(&clk->cdev, &posix_clock_file_operations);
169 0 : err = cdev_device_add(&clk->cdev, dev);
170 0 : if (err) {
171 0 : pr_err("%s unable to add device %d:%d\n",
172 : dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt));
173 0 : return err;
174 : }
175 0 : clk->cdev.owner = clk->ops.owner;
176 0 : clk->dev = dev;
177 :
178 0 : return 0;
179 : }
180 : EXPORT_SYMBOL_GPL(posix_clock_register);
181 :
182 0 : void posix_clock_unregister(struct posix_clock *clk)
183 : {
184 0 : cdev_device_del(&clk->cdev, clk->dev);
185 :
186 0 : down_write(&clk->rwsem);
187 0 : clk->zombie = true;
188 0 : up_write(&clk->rwsem);
189 :
190 0 : put_device(clk->dev);
191 0 : }
192 : EXPORT_SYMBOL_GPL(posix_clock_unregister);
193 :
194 : struct posix_clock_desc {
195 : struct file *fp;
196 : struct posix_clock *clk;
197 : };
198 :
199 0 : static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
200 : {
201 0 : struct file *fp = fget(clockid_to_fd(id));
202 0 : int err = -EINVAL;
203 :
204 0 : if (!fp)
205 : return err;
206 :
207 0 : if (fp->f_op->open != posix_clock_open || !fp->private_data)
208 : goto out;
209 :
210 0 : cd->fp = fp;
211 0 : cd->clk = get_posix_clock(fp);
212 :
213 0 : err = cd->clk ? 0 : -ENODEV;
214 : out:
215 0 : if (err)
216 0 : fput(fp);
217 : return err;
218 : }
219 :
220 : static void put_clock_desc(struct posix_clock_desc *cd)
221 : {
222 0 : put_posix_clock(cd->clk);
223 0 : fput(cd->fp);
224 : }
225 :
226 0 : static int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx)
227 : {
228 : struct posix_clock_desc cd;
229 : int err;
230 :
231 0 : err = get_clock_desc(id, &cd);
232 0 : if (err)
233 : return err;
234 :
235 0 : if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
236 : err = -EACCES;
237 : goto out;
238 : }
239 :
240 0 : if (cd.clk->ops.clock_adjtime)
241 0 : err = cd.clk->ops.clock_adjtime(cd.clk, tx);
242 : else
243 : err = -EOPNOTSUPP;
244 : out:
245 0 : put_clock_desc(&cd);
246 :
247 0 : return err;
248 : }
249 :
250 0 : static int pc_clock_gettime(clockid_t id, struct timespec64 *ts)
251 : {
252 : struct posix_clock_desc cd;
253 : int err;
254 :
255 0 : err = get_clock_desc(id, &cd);
256 0 : if (err)
257 : return err;
258 :
259 0 : if (cd.clk->ops.clock_gettime)
260 0 : err = cd.clk->ops.clock_gettime(cd.clk, ts);
261 : else
262 : err = -EOPNOTSUPP;
263 :
264 0 : put_clock_desc(&cd);
265 :
266 0 : return err;
267 : }
268 :
269 0 : static int pc_clock_getres(clockid_t id, struct timespec64 *ts)
270 : {
271 : struct posix_clock_desc cd;
272 : int err;
273 :
274 0 : err = get_clock_desc(id, &cd);
275 0 : if (err)
276 : return err;
277 :
278 0 : if (cd.clk->ops.clock_getres)
279 0 : err = cd.clk->ops.clock_getres(cd.clk, ts);
280 : else
281 : err = -EOPNOTSUPP;
282 :
283 0 : put_clock_desc(&cd);
284 :
285 0 : return err;
286 : }
287 :
288 0 : static int pc_clock_settime(clockid_t id, const struct timespec64 *ts)
289 : {
290 : struct posix_clock_desc cd;
291 : int err;
292 :
293 0 : err = get_clock_desc(id, &cd);
294 0 : if (err)
295 : return err;
296 :
297 0 : if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
298 : err = -EACCES;
299 : goto out;
300 : }
301 :
302 0 : if (cd.clk->ops.clock_settime)
303 0 : err = cd.clk->ops.clock_settime(cd.clk, ts);
304 : else
305 : err = -EOPNOTSUPP;
306 : out:
307 0 : put_clock_desc(&cd);
308 :
309 0 : return err;
310 : }
311 :
312 : const struct k_clock clock_posix_dynamic = {
313 : .clock_getres = pc_clock_getres,
314 : .clock_set = pc_clock_settime,
315 : .clock_get_timespec = pc_clock_gettime,
316 : .clock_adj = pc_clock_adjtime,
317 : };
|