0001
0002
0003
0004
0005
0006
0007 #include <linux/device.h>
0008 #include <linux/export.h>
0009 #include <linux/file.h>
0010 #include <linux/posix-clock.h>
0011 #include <linux/slab.h>
0012 #include <linux/syscalls.h>
0013 #include <linux/uaccess.h>
0014
0015 #include "posix-timers.h"
0016
0017
0018
0019
0020 static struct posix_clock *get_posix_clock(struct file *fp)
0021 {
0022 struct posix_clock *clk = fp->private_data;
0023
0024 down_read(&clk->rwsem);
0025
0026 if (!clk->zombie)
0027 return clk;
0028
0029 up_read(&clk->rwsem);
0030
0031 return NULL;
0032 }
0033
0034 static void put_posix_clock(struct posix_clock *clk)
0035 {
0036 up_read(&clk->rwsem);
0037 }
0038
0039 static ssize_t posix_clock_read(struct file *fp, char __user *buf,
0040 size_t count, loff_t *ppos)
0041 {
0042 struct posix_clock *clk = get_posix_clock(fp);
0043 int err = -EINVAL;
0044
0045 if (!clk)
0046 return -ENODEV;
0047
0048 if (clk->ops.read)
0049 err = clk->ops.read(clk, fp->f_flags, buf, count);
0050
0051 put_posix_clock(clk);
0052
0053 return err;
0054 }
0055
0056 static __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
0057 {
0058 struct posix_clock *clk = get_posix_clock(fp);
0059 __poll_t result = 0;
0060
0061 if (!clk)
0062 return EPOLLERR;
0063
0064 if (clk->ops.poll)
0065 result = clk->ops.poll(clk, fp, wait);
0066
0067 put_posix_clock(clk);
0068
0069 return result;
0070 }
0071
0072 static long posix_clock_ioctl(struct file *fp,
0073 unsigned int cmd, unsigned long arg)
0074 {
0075 struct posix_clock *clk = get_posix_clock(fp);
0076 int err = -ENOTTY;
0077
0078 if (!clk)
0079 return -ENODEV;
0080
0081 if (clk->ops.ioctl)
0082 err = clk->ops.ioctl(clk, cmd, arg);
0083
0084 put_posix_clock(clk);
0085
0086 return err;
0087 }
0088
0089 #ifdef CONFIG_COMPAT
0090 static long posix_clock_compat_ioctl(struct file *fp,
0091 unsigned int cmd, unsigned long arg)
0092 {
0093 struct posix_clock *clk = get_posix_clock(fp);
0094 int err = -ENOTTY;
0095
0096 if (!clk)
0097 return -ENODEV;
0098
0099 if (clk->ops.ioctl)
0100 err = clk->ops.ioctl(clk, cmd, arg);
0101
0102 put_posix_clock(clk);
0103
0104 return err;
0105 }
0106 #endif
0107
0108 static int posix_clock_open(struct inode *inode, struct file *fp)
0109 {
0110 int err;
0111 struct posix_clock *clk =
0112 container_of(inode->i_cdev, struct posix_clock, cdev);
0113
0114 down_read(&clk->rwsem);
0115
0116 if (clk->zombie) {
0117 err = -ENODEV;
0118 goto out;
0119 }
0120 if (clk->ops.open)
0121 err = clk->ops.open(clk, fp->f_mode);
0122 else
0123 err = 0;
0124
0125 if (!err) {
0126 get_device(clk->dev);
0127 fp->private_data = clk;
0128 }
0129 out:
0130 up_read(&clk->rwsem);
0131 return err;
0132 }
0133
0134 static int posix_clock_release(struct inode *inode, struct file *fp)
0135 {
0136 struct posix_clock *clk = fp->private_data;
0137 int err = 0;
0138
0139 if (clk->ops.release)
0140 err = clk->ops.release(clk);
0141
0142 put_device(clk->dev);
0143
0144 fp->private_data = NULL;
0145
0146 return err;
0147 }
0148
0149 static const struct file_operations posix_clock_file_operations = {
0150 .owner = THIS_MODULE,
0151 .llseek = no_llseek,
0152 .read = posix_clock_read,
0153 .poll = posix_clock_poll,
0154 .unlocked_ioctl = posix_clock_ioctl,
0155 .open = posix_clock_open,
0156 .release = posix_clock_release,
0157 #ifdef CONFIG_COMPAT
0158 .compat_ioctl = posix_clock_compat_ioctl,
0159 #endif
0160 };
0161
0162 int posix_clock_register(struct posix_clock *clk, struct device *dev)
0163 {
0164 int err;
0165
0166 init_rwsem(&clk->rwsem);
0167
0168 cdev_init(&clk->cdev, &posix_clock_file_operations);
0169 err = cdev_device_add(&clk->cdev, dev);
0170 if (err) {
0171 pr_err("%s unable to add device %d:%d\n",
0172 dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt));
0173 return err;
0174 }
0175 clk->cdev.owner = clk->ops.owner;
0176 clk->dev = dev;
0177
0178 return 0;
0179 }
0180 EXPORT_SYMBOL_GPL(posix_clock_register);
0181
0182 void posix_clock_unregister(struct posix_clock *clk)
0183 {
0184 cdev_device_del(&clk->cdev, clk->dev);
0185
0186 down_write(&clk->rwsem);
0187 clk->zombie = true;
0188 up_write(&clk->rwsem);
0189
0190 put_device(clk->dev);
0191 }
0192 EXPORT_SYMBOL_GPL(posix_clock_unregister);
0193
0194 struct posix_clock_desc {
0195 struct file *fp;
0196 struct posix_clock *clk;
0197 };
0198
0199 static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
0200 {
0201 struct file *fp = fget(clockid_to_fd(id));
0202 int err = -EINVAL;
0203
0204 if (!fp)
0205 return err;
0206
0207 if (fp->f_op->open != posix_clock_open || !fp->private_data)
0208 goto out;
0209
0210 cd->fp = fp;
0211 cd->clk = get_posix_clock(fp);
0212
0213 err = cd->clk ? 0 : -ENODEV;
0214 out:
0215 if (err)
0216 fput(fp);
0217 return err;
0218 }
0219
0220 static void put_clock_desc(struct posix_clock_desc *cd)
0221 {
0222 put_posix_clock(cd->clk);
0223 fput(cd->fp);
0224 }
0225
0226 static int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx)
0227 {
0228 struct posix_clock_desc cd;
0229 int err;
0230
0231 err = get_clock_desc(id, &cd);
0232 if (err)
0233 return err;
0234
0235 if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
0236 err = -EACCES;
0237 goto out;
0238 }
0239
0240 if (cd.clk->ops.clock_adjtime)
0241 err = cd.clk->ops.clock_adjtime(cd.clk, tx);
0242 else
0243 err = -EOPNOTSUPP;
0244 out:
0245 put_clock_desc(&cd);
0246
0247 return err;
0248 }
0249
0250 static int pc_clock_gettime(clockid_t id, struct timespec64 *ts)
0251 {
0252 struct posix_clock_desc cd;
0253 int err;
0254
0255 err = get_clock_desc(id, &cd);
0256 if (err)
0257 return err;
0258
0259 if (cd.clk->ops.clock_gettime)
0260 err = cd.clk->ops.clock_gettime(cd.clk, ts);
0261 else
0262 err = -EOPNOTSUPP;
0263
0264 put_clock_desc(&cd);
0265
0266 return err;
0267 }
0268
0269 static int pc_clock_getres(clockid_t id, struct timespec64 *ts)
0270 {
0271 struct posix_clock_desc cd;
0272 int err;
0273
0274 err = get_clock_desc(id, &cd);
0275 if (err)
0276 return err;
0277
0278 if (cd.clk->ops.clock_getres)
0279 err = cd.clk->ops.clock_getres(cd.clk, ts);
0280 else
0281 err = -EOPNOTSUPP;
0282
0283 put_clock_desc(&cd);
0284
0285 return err;
0286 }
0287
0288 static int pc_clock_settime(clockid_t id, const struct timespec64 *ts)
0289 {
0290 struct posix_clock_desc cd;
0291 int err;
0292
0293 err = get_clock_desc(id, &cd);
0294 if (err)
0295 return err;
0296
0297 if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
0298 err = -EACCES;
0299 goto out;
0300 }
0301
0302 if (cd.clk->ops.clock_settime)
0303 err = cd.clk->ops.clock_settime(cd.clk, ts);
0304 else
0305 err = -EOPNOTSUPP;
0306 out:
0307 put_clock_desc(&cd);
0308
0309 return err;
0310 }
0311
0312 const struct k_clock clock_posix_dynamic = {
0313 .clock_getres = pc_clock_getres,
0314 .clock_set = pc_clock_settime,
0315 .clock_get_timespec = pc_clock_gettime,
0316 .clock_adj = pc_clock_adjtime,
0317 };