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
*clk
= fp
->private_data
;
24 down_read(&clk
->rwsem
);
34 static void put_posix_clock(struct posix_clock
*clk
)
39 static ssize_t
posix_clock_read(struct file
*fp
, char __user
*buf
,
40 size_t count
, loff_t
*ppos
)
42 struct posix_clock
*clk
= get_posix_clock(fp
);
49 err
= clk
->ops
.read(clk
, fp
->f_flags
, buf
, count
);
56 static __poll_t
posix_clock_poll(struct file
*fp
, poll_table
*wait
)
58 struct posix_clock
*clk
= get_posix_clock(fp
);
65 result
= clk
->ops
.poll(clk
, fp
, wait
);
72 static long posix_clock_ioctl(struct file
*fp
,
73 unsigned int cmd
, unsigned long arg
)
75 struct posix_clock
*clk
= get_posix_clock(fp
);
82 err
= clk
->ops
.ioctl(clk
, cmd
, arg
);
90 static long posix_clock_compat_ioctl(struct file
*fp
,
91 unsigned int cmd
, unsigned long arg
)
93 struct posix_clock
*clk
= get_posix_clock(fp
);
100 err
= clk
->ops
.ioctl(clk
, cmd
, arg
);
102 put_posix_clock(clk
);
108 static int posix_clock_open(struct inode
*inode
, struct file
*fp
)
111 struct posix_clock
*clk
=
112 container_of(inode
->i_cdev
, struct posix_clock
, cdev
);
114 down_read(&clk
->rwsem
);
121 err
= clk
->ops
.open(clk
, fp
->f_mode
);
126 get_device(clk
->dev
);
127 fp
->private_data
= clk
;
130 up_read(&clk
->rwsem
);
134 static int posix_clock_release(struct inode
*inode
, struct file
*fp
)
136 struct posix_clock
*clk
= fp
->private_data
;
139 if (clk
->ops
.release
)
140 err
= clk
->ops
.release(clk
);
142 put_device(clk
->dev
);
144 fp
->private_data
= NULL
;
149 static const struct file_operations posix_clock_file_operations
= {
150 .owner
= THIS_MODULE
,
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
,
158 .compat_ioctl
= posix_clock_compat_ioctl
,
162 int posix_clock_register(struct posix_clock
*clk
, struct device
*dev
)
166 init_rwsem(&clk
->rwsem
);
168 cdev_init(&clk
->cdev
, &posix_clock_file_operations
);
169 err
= cdev_device_add(&clk
->cdev
, dev
);
171 pr_err("%s unable to add device %d:%d\n",
172 dev_name(dev
), MAJOR(dev
->devt
), MINOR(dev
->devt
));
175 clk
->cdev
.owner
= clk
->ops
.owner
;
180 EXPORT_SYMBOL_GPL(posix_clock_register
);
182 void posix_clock_unregister(struct posix_clock
*clk
)
184 cdev_device_del(&clk
->cdev
, clk
->dev
);
186 down_write(&clk
->rwsem
);
188 up_write(&clk
->rwsem
);
190 put_device(clk
->dev
);
192 EXPORT_SYMBOL_GPL(posix_clock_unregister
);
194 struct posix_clock_desc
{
196 struct posix_clock
*clk
;
199 static int get_clock_desc(const clockid_t id
, struct posix_clock_desc
*cd
)
201 struct file
*fp
= fget(clockid_to_fd(id
));
207 if (fp
->f_op
->open
!= posix_clock_open
|| !fp
->private_data
)
211 cd
->clk
= get_posix_clock(fp
);
213 err
= cd
->clk
? 0 : -ENODEV
;
220 static void put_clock_desc(struct posix_clock_desc
*cd
)
222 put_posix_clock(cd
->clk
);
226 static int pc_clock_adjtime(clockid_t id
, struct __kernel_timex
*tx
)
228 struct posix_clock_desc cd
;
231 err
= get_clock_desc(id
, &cd
);
235 if ((cd
.fp
->f_mode
& FMODE_WRITE
) == 0) {
240 if (cd
.clk
->ops
.clock_adjtime
)
241 err
= cd
.clk
->ops
.clock_adjtime(cd
.clk
, tx
);
250 static int pc_clock_gettime(clockid_t id
, struct timespec64
*ts
)
252 struct posix_clock_desc cd
;
255 err
= get_clock_desc(id
, &cd
);
259 if (cd
.clk
->ops
.clock_gettime
)
260 err
= cd
.clk
->ops
.clock_gettime(cd
.clk
, ts
);
269 static int pc_clock_getres(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_getres
)
279 err
= cd
.clk
->ops
.clock_getres(cd
.clk
, ts
);
288 static int pc_clock_settime(clockid_t id
, const struct timespec64
*ts
)
290 struct posix_clock_desc cd
;
293 err
= get_clock_desc(id
, &cd
);
297 if ((cd
.fp
->f_mode
& FMODE_WRITE
) == 0) {
302 if (cd
.clk
->ops
.clock_settime
)
303 err
= cd
.clk
->ops
.clock_settime(cd
.clk
, ts
);
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
,