1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Support for dynamic clock devices 4 * 5 * Copyright (C) 2010 OMICRON electronics GmbH 6 */ 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> 14 15 #include "posix-timers.h" 16 17 /* 18 * Returns NULL if the posix_clock instance attached to 'fp' is old and stale. 19 */ 20 static struct posix_clock *get_posix_clock(struct file *fp) 21 { 22 struct posix_clock_context *pccontext = fp->private_data; 23 struct posix_clock *clk = pccontext->clk; 24 25 down_read(&clk->rwsem); 26 27 if (!clk->zombie) 28 return clk; 29 30 up_read(&clk->rwsem); 31 32 return NULL; 33 } 34 35 static void put_posix_clock(struct posix_clock *clk) 36 { 37 up_read(&clk->rwsem); 38 } 39 40 static ssize_t posix_clock_read(struct file *fp, char __user *buf, 41 size_t count, loff_t *ppos) 42 { 43 struct posix_clock_context *pccontext = fp->private_data; 44 struct posix_clock *clk = get_posix_clock(fp); 45 int err = -EINVAL; 46 47 if (!clk) 48 return -ENODEV; 49 50 if (clk->ops.read) 51 err = clk->ops.read(pccontext, fp->f_flags, buf, count); 52 53 put_posix_clock(clk); 54 55 return err; 56 } 57 58 static __poll_t posix_clock_poll(struct file *fp, poll_table *wait) 59 { 60 struct posix_clock_context *pccontext = fp->private_data; 61 struct posix_clock *clk = get_posix_clock(fp); 62 __poll_t result = 0; 63 64 if (!clk) 65 return EPOLLERR; 66 67 if (clk->ops.poll) 68 result = clk->ops.poll(pccontext, fp, wait); 69 70 put_posix_clock(clk); 71 72 return result; 73 } 74 75 static long posix_clock_ioctl(struct file *fp, 76 unsigned int cmd, unsigned long arg) 77 { 78 struct posix_clock_context *pccontext = fp->private_data; 79 struct posix_clock *clk = get_posix_clock(fp); 80 int err = -ENOTTY; 81 82 if (!clk) 83 return -ENODEV; 84 85 if (clk->ops.ioctl) 86 err = clk->ops.ioctl(pccontext, cmd, arg); 87 88 put_posix_clock(clk); 89 90 return err; 91 } 92 93 #ifdef CONFIG_COMPAT 94 static long posix_clock_compat_ioctl(struct file *fp, 95 unsigned int cmd, unsigned long arg) 96 { 97 struct posix_clock_context *pccontext = fp->private_data; 98 struct posix_clock *clk = get_posix_clock(fp); 99 int err = -ENOTTY; 100 101 if (!clk) 102 return -ENODEV; 103 104 if (clk->ops.ioctl) 105 err = clk->ops.ioctl(pccontext, cmd, arg); 106 107 put_posix_clock(clk); 108 109 return err; 110 } 111 #endif 112 113 static int posix_clock_open(struct inode *inode, struct file *fp) 114 { 115 int err; 116 struct posix_clock *clk = 117 container_of(inode->i_cdev, struct posix_clock, cdev); 118 struct posix_clock_context *pccontext; 119 120 down_read(&clk->rwsem); 121 122 if (clk->zombie) { 123 err = -ENODEV; 124 goto out; 125 } 126 pccontext = kzalloc(sizeof(*pccontext), GFP_KERNEL); 127 if (!pccontext) { 128 err = -ENOMEM; 129 goto out; 130 } 131 pccontext->clk = clk; 132 if (clk->ops.open) { 133 err = clk->ops.open(pccontext, fp->f_mode); 134 if (err) { 135 kfree(pccontext); 136 goto out; 137 } 138 } 139 140 fp->private_data = pccontext; 141 get_device(clk->dev); 142 err = 0; 143 out: 144 up_read(&clk->rwsem); 145 return err; 146 } 147 148 static int posix_clock_release(struct inode *inode, struct file *fp) 149 { 150 struct posix_clock_context *pccontext = fp->private_data; 151 struct posix_clock *clk; 152 int err = 0; 153 154 if (!pccontext) 155 return -ENODEV; 156 clk = pccontext->clk; 157 158 if (clk->ops.release) 159 err = clk->ops.release(pccontext); 160 161 put_device(clk->dev); 162 163 kfree(pccontext); 164 fp->private_data = NULL; 165 166 return err; 167 } 168 169 static const struct file_operations posix_clock_file_operations = { 170 .owner = THIS_MODULE, 171 .llseek = no_llseek, 172 .read = posix_clock_read, 173 .poll = posix_clock_poll, 174 .unlocked_ioctl = posix_clock_ioctl, 175 .open = posix_clock_open, 176 .release = posix_clock_release, 177 #ifdef CONFIG_COMPAT 178 .compat_ioctl = posix_clock_compat_ioctl, 179 #endif 180 }; 181 182 int posix_clock_register(struct posix_clock *clk, struct device *dev) 183 { 184 int err; 185 186 init_rwsem(&clk->rwsem); 187 188 cdev_init(&clk->cdev, &posix_clock_file_operations); 189 err = cdev_device_add(&clk->cdev, dev); 190 if (err) { 191 pr_err("%s unable to add device %d:%d\n", 192 dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt)); 193 return err; 194 } 195 clk->cdev.owner = clk->ops.owner; 196 clk->dev = dev; 197 198 return 0; 199 } 200 EXPORT_SYMBOL_GPL(posix_clock_register); 201 202 void posix_clock_unregister(struct posix_clock *clk) 203 { 204 cdev_device_del(&clk->cdev, clk->dev); 205 206 down_write(&clk->rwsem); 207 clk->zombie = true; 208 up_write(&clk->rwsem); 209 210 put_device(clk->dev); 211 } 212 EXPORT_SYMBOL_GPL(posix_clock_unregister); 213 214 struct posix_clock_desc { 215 struct file *fp; 216 struct posix_clock *clk; 217 }; 218 219 static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd) 220 { 221 struct file *fp = fget(clockid_to_fd(id)); 222 int err = -EINVAL; 223 224 if (!fp) 225 return err; 226 227 if (fp->f_op->open != posix_clock_open || !fp->private_data) 228 goto out; 229 230 cd->fp = fp; 231 cd->clk = get_posix_clock(fp); 232 233 err = cd->clk ? 0 : -ENODEV; 234 out: 235 if (err) 236 fput(fp); 237 return err; 238 } 239 240 static void put_clock_desc(struct posix_clock_desc *cd) 241 { 242 put_posix_clock(cd->clk); 243 fput(cd->fp); 244 } 245 246 static int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx) 247 { 248 struct posix_clock_desc cd; 249 int err; 250 251 err = get_clock_desc(id, &cd); 252 if (err) 253 return err; 254 255 if ((cd.fp->f_mode & FMODE_WRITE) == 0) { 256 err = -EACCES; 257 goto out; 258 } 259 260 if (cd.clk->ops.clock_adjtime) 261 err = cd.clk->ops.clock_adjtime(cd.clk, tx); 262 else 263 err = -EOPNOTSUPP; 264 out: 265 put_clock_desc(&cd); 266 267 return err; 268 } 269 270 static int pc_clock_gettime(clockid_t id, struct timespec64 *ts) 271 { 272 struct posix_clock_desc cd; 273 int err; 274 275 err = get_clock_desc(id, &cd); 276 if (err) 277 return err; 278 279 if (cd.clk->ops.clock_gettime) 280 err = cd.clk->ops.clock_gettime(cd.clk, ts); 281 else 282 err = -EOPNOTSUPP; 283 284 put_clock_desc(&cd); 285 286 return err; 287 } 288 289 static int pc_clock_getres(clockid_t id, struct timespec64 *ts) 290 { 291 struct posix_clock_desc cd; 292 int err; 293 294 err = get_clock_desc(id, &cd); 295 if (err) 296 return err; 297 298 if (cd.clk->ops.clock_getres) 299 err = cd.clk->ops.clock_getres(cd.clk, ts); 300 else 301 err = -EOPNOTSUPP; 302 303 put_clock_desc(&cd); 304 305 return err; 306 } 307 308 static int pc_clock_settime(clockid_t id, const struct timespec64 *ts) 309 { 310 struct posix_clock_desc cd; 311 int err; 312 313 err = get_clock_desc(id, &cd); 314 if (err) 315 return err; 316 317 if ((cd.fp->f_mode & FMODE_WRITE) == 0) { 318 err = -EACCES; 319 goto out; 320 } 321 322 if (cd.clk->ops.clock_settime) 323 err = cd.clk->ops.clock_settime(cd.clk, ts); 324 else 325 err = -EOPNOTSUPP; 326 out: 327 put_clock_desc(&cd); 328 329 return err; 330 } 331 332 const struct k_clock clock_posix_dynamic = { 333 .clock_getres = pc_clock_getres, 334 .clock_set = pc_clock_settime, 335 .clock_get_timespec = pc_clock_gettime, 336 .clock_adj = pc_clock_adjtime, 337 }; 338