1 // SPDX-License-Identifier: GPL-2.0+
3 * Support for dynamic clock devices
5 * Copyright (C) 2010 OMICRON electronics GmbH
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>
15 #include "posix-timers.h"
18 * Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
20 static struct posix_clock
*get_posix_clock(struct file
*fp
)
22 struct posix_clock_context
*pccontext
= fp
->private_data
;
23 struct posix_clock
*clk
= pccontext
->clk
;
25 down_read(&clk
->rwsem
);
35 static void put_posix_clock(struct posix_clock
*clk
)
40 static ssize_t
posix_clock_read(struct file
*fp
, char __user
*buf
,
41 size_t count
, loff_t
*ppos
)
43 struct posix_clock_context
*pccontext
= fp
->private_data
;
44 struct posix_clock
*clk
= get_posix_clock(fp
);
51 err
= clk
->ops
.read(pccontext
, fp
->f_flags
, buf
, count
);
58 static __poll_t
posix_clock_poll(struct file
*fp
, poll_table
*wait
)
60 struct posix_clock_context
*pccontext
= fp
->private_data
;
61 struct posix_clock
*clk
= get_posix_clock(fp
);
68 result
= clk
->ops
.poll(pccontext
, fp
, wait
);
75 static long posix_clock_ioctl(struct file
*fp
,
76 unsigned int cmd
, unsigned long arg
)
78 struct posix_clock_context
*pccontext
= fp
->private_data
;
79 struct posix_clock
*clk
= get_posix_clock(fp
);
86 err
= clk
->ops
.ioctl(pccontext
, cmd
, arg
);
94 static long posix_clock_compat_ioctl(struct file
*fp
,
95 unsigned int cmd
, unsigned long arg
)
97 struct posix_clock_context
*pccontext
= fp
->private_data
;
98 struct posix_clock
*clk
= get_posix_clock(fp
);
105 err
= clk
->ops
.ioctl(pccontext
, cmd
, arg
);
107 put_posix_clock(clk
);
113 static int posix_clock_open(struct inode
*inode
, struct file
*fp
)
116 struct posix_clock
*clk
=
117 container_of(inode
->i_cdev
, struct posix_clock
, cdev
);
118 struct posix_clock_context
*pccontext
;
120 down_read(&clk
->rwsem
);
126 pccontext
= kzalloc(sizeof(*pccontext
), GFP_KERNEL
);
131 pccontext
->clk
= clk
;
133 err
= clk
->ops
.open(pccontext
, fp
->f_mode
);
140 fp
->private_data
= pccontext
;
141 get_device(clk
->dev
);
144 up_read(&clk
->rwsem
);
148 static int posix_clock_release(struct inode
*inode
, struct file
*fp
)
150 struct posix_clock_context
*pccontext
= fp
->private_data
;
151 struct posix_clock
*clk
;
156 clk
= pccontext
->clk
;
158 if (clk
->ops
.release
)
159 err
= clk
->ops
.release(pccontext
);
161 put_device(clk
->dev
);
164 fp
->private_data
= NULL
;
169 static const struct file_operations posix_clock_file_operations
= {
170 .owner
= THIS_MODULE
,
171 .read
= posix_clock_read
,
172 .poll
= posix_clock_poll
,
173 .unlocked_ioctl
= posix_clock_ioctl
,
174 .open
= posix_clock_open
,
175 .release
= posix_clock_release
,
177 .compat_ioctl
= posix_clock_compat_ioctl
,
181 int posix_clock_register(struct posix_clock
*clk
, struct device
*dev
)
185 init_rwsem(&clk
->rwsem
);
187 cdev_init(&clk
->cdev
, &posix_clock_file_operations
);
188 err
= cdev_device_add(&clk
->cdev
, dev
);
190 pr_err("%s unable to add device %d:%d\n",
191 dev_name(dev
), MAJOR(dev
->devt
), MINOR(dev
->devt
));
194 clk
->cdev
.owner
= clk
->ops
.owner
;
199 EXPORT_SYMBOL_GPL(posix_clock_register
);
201 void posix_clock_unregister(struct posix_clock
*clk
)
203 cdev_device_del(&clk
->cdev
, clk
->dev
);
205 down_write(&clk
->rwsem
);
207 up_write(&clk
->rwsem
);
209 put_device(clk
->dev
);
211 EXPORT_SYMBOL_GPL(posix_clock_unregister
);
213 struct posix_clock_desc
{
215 struct posix_clock
*clk
;
218 static int get_clock_desc(const clockid_t id
, struct posix_clock_desc
*cd
)
220 struct file
*fp
= fget(clockid_to_fd(id
));
226 if (fp
->f_op
->open
!= posix_clock_open
|| !fp
->private_data
)
230 cd
->clk
= get_posix_clock(fp
);
232 err
= cd
->clk
? 0 : -ENODEV
;
239 static void put_clock_desc(struct posix_clock_desc
*cd
)
241 put_posix_clock(cd
->clk
);
245 static int pc_clock_adjtime(clockid_t id
, struct __kernel_timex
*tx
)
247 struct posix_clock_desc cd
;
250 err
= get_clock_desc(id
, &cd
);
254 if ((cd
.fp
->f_mode
& FMODE_WRITE
) == 0) {
259 if (cd
.clk
->ops
.clock_adjtime
)
260 err
= cd
.clk
->ops
.clock_adjtime(cd
.clk
, tx
);
269 static int pc_clock_gettime(clockid_t id
, struct timespec64
*ts
)
271 struct posix_clock_desc cd
;
274 err
= get_clock_desc(id
, &cd
);
278 if (cd
.clk
->ops
.clock_gettime
)
279 err
= cd
.clk
->ops
.clock_gettime(cd
.clk
, ts
);
288 static int pc_clock_getres(clockid_t id
, struct timespec64
*ts
)
290 struct posix_clock_desc cd
;
293 err
= get_clock_desc(id
, &cd
);
297 if (cd
.clk
->ops
.clock_getres
)
298 err
= cd
.clk
->ops
.clock_getres(cd
.clk
, ts
);
307 static int pc_clock_settime(clockid_t id
, const struct timespec64
*ts
)
309 struct posix_clock_desc cd
;
312 if (!timespec64_valid_strict(ts
))
315 err
= get_clock_desc(id
, &cd
);
319 if ((cd
.fp
->f_mode
& FMODE_WRITE
) == 0) {
324 if (cd
.clk
->ops
.clock_settime
)
325 err
= cd
.clk
->ops
.clock_settime(cd
.clk
, ts
);
334 const struct k_clock clock_posix_dynamic
= {
335 .clock_getres
= pc_clock_getres
,
336 .clock_set
= pc_clock_settime
,
337 .clock_get_timespec
= pc_clock_gettime
,
338 .clock_adj
= pc_clock_adjtime
,