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"
17 static void delete_clock(struct kref
*kref
);
20 * Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
22 static struct posix_clock
*get_posix_clock(struct file
*fp
)
24 struct posix_clock
*clk
= fp
->private_data
;
26 down_read(&clk
->rwsem
);
36 static void put_posix_clock(struct posix_clock
*clk
)
41 static ssize_t
posix_clock_read(struct file
*fp
, char __user
*buf
,
42 size_t count
, loff_t
*ppos
)
44 struct posix_clock
*clk
= get_posix_clock(fp
);
51 err
= clk
->ops
.read(clk
, fp
->f_flags
, buf
, count
);
58 static __poll_t
posix_clock_poll(struct file
*fp
, poll_table
*wait
)
60 struct posix_clock
*clk
= get_posix_clock(fp
);
67 result
= clk
->ops
.poll(clk
, fp
, wait
);
74 static long posix_clock_ioctl(struct file
*fp
,
75 unsigned int cmd
, unsigned long arg
)
77 struct posix_clock
*clk
= get_posix_clock(fp
);
84 err
= clk
->ops
.ioctl(clk
, cmd
, arg
);
92 static long posix_clock_compat_ioctl(struct file
*fp
,
93 unsigned int cmd
, unsigned long arg
)
95 struct posix_clock
*clk
= get_posix_clock(fp
);
102 err
= clk
->ops
.ioctl(clk
, cmd
, arg
);
104 put_posix_clock(clk
);
110 static int posix_clock_open(struct inode
*inode
, struct file
*fp
)
113 struct posix_clock
*clk
=
114 container_of(inode
->i_cdev
, struct posix_clock
, cdev
);
116 down_read(&clk
->rwsem
);
123 err
= clk
->ops
.open(clk
, fp
->f_mode
);
128 kref_get(&clk
->kref
);
129 fp
->private_data
= clk
;
132 up_read(&clk
->rwsem
);
136 static int posix_clock_release(struct inode
*inode
, struct file
*fp
)
138 struct posix_clock
*clk
= fp
->private_data
;
141 if (clk
->ops
.release
)
142 err
= clk
->ops
.release(clk
);
144 kref_put(&clk
->kref
, delete_clock
);
146 fp
->private_data
= NULL
;
151 static const struct file_operations posix_clock_file_operations
= {
152 .owner
= THIS_MODULE
,
154 .read
= posix_clock_read
,
155 .poll
= posix_clock_poll
,
156 .unlocked_ioctl
= posix_clock_ioctl
,
157 .open
= posix_clock_open
,
158 .release
= posix_clock_release
,
160 .compat_ioctl
= posix_clock_compat_ioctl
,
164 int posix_clock_register(struct posix_clock
*clk
, dev_t devid
)
168 kref_init(&clk
->kref
);
169 init_rwsem(&clk
->rwsem
);
171 cdev_init(&clk
->cdev
, &posix_clock_file_operations
);
172 clk
->cdev
.owner
= clk
->ops
.owner
;
173 err
= cdev_add(&clk
->cdev
, devid
, 1);
177 EXPORT_SYMBOL_GPL(posix_clock_register
);
179 static void delete_clock(struct kref
*kref
)
181 struct posix_clock
*clk
= container_of(kref
, struct posix_clock
, kref
);
187 void posix_clock_unregister(struct posix_clock
*clk
)
189 cdev_del(&clk
->cdev
);
191 down_write(&clk
->rwsem
);
193 up_write(&clk
->rwsem
);
195 kref_put(&clk
->kref
, delete_clock
);
197 EXPORT_SYMBOL_GPL(posix_clock_unregister
);
199 struct posix_clock_desc
{
201 struct posix_clock
*clk
;
204 static int get_clock_desc(const clockid_t id
, struct posix_clock_desc
*cd
)
206 struct file
*fp
= fget(clockid_to_fd(id
));
212 if (fp
->f_op
->open
!= posix_clock_open
|| !fp
->private_data
)
216 cd
->clk
= get_posix_clock(fp
);
218 err
= cd
->clk
? 0 : -ENODEV
;
225 static void put_clock_desc(struct posix_clock_desc
*cd
)
227 put_posix_clock(cd
->clk
);
231 static int pc_clock_adjtime(clockid_t id
, struct __kernel_timex
*tx
)
233 struct posix_clock_desc cd
;
236 err
= get_clock_desc(id
, &cd
);
240 if ((cd
.fp
->f_mode
& FMODE_WRITE
) == 0) {
245 if (cd
.clk
->ops
.clock_adjtime
)
246 err
= cd
.clk
->ops
.clock_adjtime(cd
.clk
, tx
);
255 static int pc_clock_gettime(clockid_t id
, struct timespec64
*ts
)
257 struct posix_clock_desc cd
;
260 err
= get_clock_desc(id
, &cd
);
264 if (cd
.clk
->ops
.clock_gettime
)
265 err
= cd
.clk
->ops
.clock_gettime(cd
.clk
, ts
);
274 static int pc_clock_getres(clockid_t id
, struct timespec64
*ts
)
276 struct posix_clock_desc cd
;
279 err
= get_clock_desc(id
, &cd
);
283 if (cd
.clk
->ops
.clock_getres
)
284 err
= cd
.clk
->ops
.clock_getres(cd
.clk
, ts
);
293 static int pc_clock_settime(clockid_t id
, const struct timespec64
*ts
)
295 struct posix_clock_desc cd
;
298 err
= get_clock_desc(id
, &cd
);
302 if ((cd
.fp
->f_mode
& FMODE_WRITE
) == 0) {
307 if (cd
.clk
->ops
.clock_settime
)
308 err
= cd
.clk
->ops
.clock_settime(cd
.clk
, ts
);
317 const struct k_clock clock_posix_dynamic
= {
318 .clock_getres
= pc_clock_getres
,
319 .clock_set
= pc_clock_settime
,
320 .clock_get
= pc_clock_gettime
,
321 .clock_adj
= pc_clock_adjtime
,