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