Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Support for dynamic clock devices
0004  *
0005  * Copyright (C) 2010 OMICRON electronics GmbH
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  * Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
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 };