1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Support for dynamic clock devices 4 * 5 * Copyright (C) 2010 OMICRON electronics GmbH 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 #include <linux/device.h> 22 #include <linux/export.h> 23 #include <linux/file.h> 24 #include <linux/posix-clock.h> 25 #include <linux/slab.h> 26 #include <linux/syscalls.h> 27 #include <linux/uaccess.h> 28 29 #include "posix-timers.h" 30 31 static void delete_clock(struct kref *kref); 32 33 /* 34 * Returns NULL if the posix_clock instance attached to 'fp' is old and stale. 35 */ 36 static struct posix_clock *get_posix_clock(struct file *fp) 37 { 38 struct posix_clock *clk = fp->private_data; 39 40 down_read(&clk->rwsem); 41 42 if (!clk->zombie) 43 return clk; 44 45 up_read(&clk->rwsem); 46 47 return NULL; 48 } 49 50 static void put_posix_clock(struct posix_clock *clk) 51 { 52 up_read(&clk->rwsem); 53 } 54 55 static ssize_t posix_clock_read(struct file *fp, char __user *buf, 56 size_t count, loff_t *ppos) 57 { 58 struct posix_clock *clk = get_posix_clock(fp); 59 int err = -EINVAL; 60 61 if (!clk) 62 return -ENODEV; 63 64 if (clk->ops.read) 65 err = clk->ops.read(clk, fp->f_flags, buf, count); 66 67 put_posix_clock(clk); 68 69 return err; 70 } 71 72 static __poll_t posix_clock_poll(struct file *fp, poll_table *wait) 73 { 74 struct posix_clock *clk = get_posix_clock(fp); 75 __poll_t result = 0; 76 77 if (!clk) 78 return EPOLLERR; 79 80 if (clk->ops.poll) 81 result = clk->ops.poll(clk, fp, wait); 82 83 put_posix_clock(clk); 84 85 return result; 86 } 87 88 static long posix_clock_ioctl(struct file *fp, 89 unsigned int cmd, unsigned long arg) 90 { 91 struct posix_clock *clk = get_posix_clock(fp); 92 int err = -ENOTTY; 93 94 if (!clk) 95 return -ENODEV; 96 97 if (clk->ops.ioctl) 98 err = clk->ops.ioctl(clk, cmd, arg); 99 100 put_posix_clock(clk); 101 102 return err; 103 } 104 105 #ifdef CONFIG_COMPAT 106 static long posix_clock_compat_ioctl(struct file *fp, 107 unsigned int cmd, unsigned long arg) 108 { 109 struct posix_clock *clk = get_posix_clock(fp); 110 int err = -ENOTTY; 111 112 if (!clk) 113 return -ENODEV; 114 115 if (clk->ops.ioctl) 116 err = clk->ops.ioctl(clk, cmd, arg); 117 118 put_posix_clock(clk); 119 120 return err; 121 } 122 #endif 123 124 static int posix_clock_open(struct inode *inode, struct file *fp) 125 { 126 int err; 127 struct posix_clock *clk = 128 container_of(inode->i_cdev, struct posix_clock, cdev); 129 130 down_read(&clk->rwsem); 131 132 if (clk->zombie) { 133 err = -ENODEV; 134 goto out; 135 } 136 if (clk->ops.open) 137 err = clk->ops.open(clk, fp->f_mode); 138 else 139 err = 0; 140 141 if (!err) { 142 kref_get(&clk->kref); 143 fp->private_data = clk; 144 } 145 out: 146 up_read(&clk->rwsem); 147 return err; 148 } 149 150 static int posix_clock_release(struct inode *inode, struct file *fp) 151 { 152 struct posix_clock *clk = fp->private_data; 153 int err = 0; 154 155 if (clk->ops.release) 156 err = clk->ops.release(clk); 157 158 kref_put(&clk->kref, delete_clock); 159 160 fp->private_data = NULL; 161 162 return err; 163 } 164 165 static const struct file_operations posix_clock_file_operations = { 166 .owner = THIS_MODULE, 167 .llseek = no_llseek, 168 .read = posix_clock_read, 169 .poll = posix_clock_poll, 170 .unlocked_ioctl = posix_clock_ioctl, 171 .open = posix_clock_open, 172 .release = posix_clock_release, 173 #ifdef CONFIG_COMPAT 174 .compat_ioctl = posix_clock_compat_ioctl, 175 #endif 176 }; 177 178 int posix_clock_register(struct posix_clock *clk, dev_t devid) 179 { 180 int err; 181 182 kref_init(&clk->kref); 183 init_rwsem(&clk->rwsem); 184 185 cdev_init(&clk->cdev, &posix_clock_file_operations); 186 clk->cdev.owner = clk->ops.owner; 187 err = cdev_add(&clk->cdev, devid, 1); 188 189 return err; 190 } 191 EXPORT_SYMBOL_GPL(posix_clock_register); 192 193 static void delete_clock(struct kref *kref) 194 { 195 struct posix_clock *clk = container_of(kref, struct posix_clock, kref); 196 197 if (clk->release) 198 clk->release(clk); 199 } 200 201 void posix_clock_unregister(struct posix_clock *clk) 202 { 203 cdev_del(&clk->cdev); 204 205 down_write(&clk->rwsem); 206 clk->zombie = true; 207 up_write(&clk->rwsem); 208 209 kref_put(&clk->kref, delete_clock); 210 } 211 EXPORT_SYMBOL_GPL(posix_clock_unregister); 212 213 struct posix_clock_desc { 214 struct file *fp; 215 struct posix_clock *clk; 216 }; 217 218 static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd) 219 { 220 struct file *fp = fget(clockid_to_fd(id)); 221 int err = -EINVAL; 222 223 if (!fp) 224 return err; 225 226 if (fp->f_op->open != posix_clock_open || !fp->private_data) 227 goto out; 228 229 cd->fp = fp; 230 cd->clk = get_posix_clock(fp); 231 232 err = cd->clk ? 0 : -ENODEV; 233 out: 234 if (err) 235 fput(fp); 236 return err; 237 } 238 239 static void put_clock_desc(struct posix_clock_desc *cd) 240 { 241 put_posix_clock(cd->clk); 242 fput(cd->fp); 243 } 244 245 static int pc_clock_adjtime(clockid_t id, struct timex *tx) 246 { 247 struct posix_clock_desc cd; 248 int err; 249 250 err = get_clock_desc(id, &cd); 251 if (err) 252 return err; 253 254 if ((cd.fp->f_mode & FMODE_WRITE) == 0) { 255 err = -EACCES; 256 goto out; 257 } 258 259 if (cd.clk->ops.clock_adjtime) 260 err = cd.clk->ops.clock_adjtime(cd.clk, tx); 261 else 262 err = -EOPNOTSUPP; 263 out: 264 put_clock_desc(&cd); 265 266 return err; 267 } 268 269 static int pc_clock_gettime(clockid_t id, struct timespec64 *ts) 270 { 271 struct posix_clock_desc cd; 272 int err; 273 274 err = get_clock_desc(id, &cd); 275 if (err) 276 return err; 277 278 if (cd.clk->ops.clock_gettime) 279 err = cd.clk->ops.clock_gettime(cd.clk, ts); 280 else 281 err = -EOPNOTSUPP; 282 283 put_clock_desc(&cd); 284 285 return err; 286 } 287 288 static int pc_clock_getres(clockid_t id, struct timespec64 *ts) 289 { 290 struct posix_clock_desc cd; 291 int err; 292 293 err = get_clock_desc(id, &cd); 294 if (err) 295 return err; 296 297 if (cd.clk->ops.clock_getres) 298 err = cd.clk->ops.clock_getres(cd.clk, ts); 299 else 300 err = -EOPNOTSUPP; 301 302 put_clock_desc(&cd); 303 304 return err; 305 } 306 307 static int pc_clock_settime(clockid_t id, const struct timespec64 *ts) 308 { 309 struct posix_clock_desc cd; 310 int err; 311 312 err = get_clock_desc(id, &cd); 313 if (err) 314 return err; 315 316 if ((cd.fp->f_mode & FMODE_WRITE) == 0) { 317 err = -EACCES; 318 goto out; 319 } 320 321 if (cd.clk->ops.clock_settime) 322 err = cd.clk->ops.clock_settime(cd.clk, ts); 323 else 324 err = -EOPNOTSUPP; 325 out: 326 put_clock_desc(&cd); 327 328 return err; 329 } 330 331 const struct k_clock clock_posix_dynamic = { 332 .clock_getres = pc_clock_getres, 333 .clock_set = pc_clock_settime, 334 .clock_get = pc_clock_gettime, 335 .clock_adj = pc_clock_adjtime, 336 }; 337