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 pccontext->fp = fp; 133 if (clk->ops.open) { 134 err = clk->ops.open(pccontext, fp->f_mode); 135 if (err) { 136 kfree(pccontext); 137 goto out; 138 } 139 } 140 141 fp->private_data = pccontext; 142 get_device(clk->dev); 143 err = 0; 144 out: 145 up_read(&clk->rwsem); 146 return err; 147 } 148 149 static int posix_clock_release(struct inode *inode, struct file *fp) 150 { 151 struct posix_clock_context *pccontext = fp->private_data; 152 struct posix_clock *clk; 153 int err = 0; 154 155 if (!pccontext) 156 return -ENODEV; 157 clk = pccontext->clk; 158 159 if (clk->ops.release) 160 err = clk->ops.release(pccontext); 161 162 put_device(clk->dev); 163 164 kfree(pccontext); 165 fp->private_data = NULL; 166 167 return err; 168 } 169 170 static const struct file_operations posix_clock_file_operations = { 171 .owner = THIS_MODULE, 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 (tx->modes && (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 if (!timespec64_valid_strict(ts)) 314 return -EINVAL; 315 316 err = get_clock_desc(id, &cd); 317 if (err) 318 return err; 319 320 if ((cd.fp->f_mode & FMODE_WRITE) == 0) { 321 err = -EACCES; 322 goto out; 323 } 324 325 if (cd.clk->ops.clock_settime) 326 err = cd.clk->ops.clock_settime(cd.clk, ts); 327 else 328 err = -EOPNOTSUPP; 329 out: 330 put_clock_desc(&cd); 331 332 return err; 333 } 334 335 const struct k_clock clock_posix_dynamic = { 336 .clock_getres = pc_clock_getres, 337 .clock_set = pc_clock_settime, 338 .clock_get_timespec = pc_clock_gettime, 339 .clock_adj = pc_clock_adjtime, 340 }; 341