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/file.h> 22 #include <linux/posix-clock.h> 23 #include <linux/slab.h> 24 #include <linux/syscalls.h> 25 #include <linux/uaccess.h> 26 27 static void delete_clock(struct kref *kref); 28 29 /* 30 * Returns NULL if the posix_clock instance attached to 'fp' is old and stale. 31 */ 32 static struct posix_clock *get_posix_clock(struct file *fp) 33 { 34 struct posix_clock *clk = fp->private_data; 35 36 down_read(&clk->rwsem); 37 38 if (!clk->zombie) 39 return clk; 40 41 up_read(&clk->rwsem); 42 43 return NULL; 44 } 45 46 static void put_posix_clock(struct posix_clock *clk) 47 { 48 up_read(&clk->rwsem); 49 } 50 51 static ssize_t posix_clock_read(struct file *fp, char __user *buf, 52 size_t count, loff_t *ppos) 53 { 54 struct posix_clock *clk = get_posix_clock(fp); 55 int err = -EINVAL; 56 57 if (!clk) 58 return -ENODEV; 59 60 if (clk->ops.read) 61 err = clk->ops.read(clk, fp->f_flags, buf, count); 62 63 put_posix_clock(clk); 64 65 return err; 66 } 67 68 static unsigned int posix_clock_poll(struct file *fp, poll_table *wait) 69 { 70 struct posix_clock *clk = get_posix_clock(fp); 71 int result = 0; 72 73 if (!clk) 74 return -ENODEV; 75 76 if (clk->ops.poll) 77 result = clk->ops.poll(clk, fp, wait); 78 79 put_posix_clock(clk); 80 81 return result; 82 } 83 84 static int posix_clock_fasync(int fd, struct file *fp, int on) 85 { 86 struct posix_clock *clk = get_posix_clock(fp); 87 int err = 0; 88 89 if (!clk) 90 return -ENODEV; 91 92 if (clk->ops.fasync) 93 err = clk->ops.fasync(clk, fd, fp, on); 94 95 put_posix_clock(clk); 96 97 return err; 98 } 99 100 static int posix_clock_mmap(struct file *fp, struct vm_area_struct *vma) 101 { 102 struct posix_clock *clk = get_posix_clock(fp); 103 int err = -ENODEV; 104 105 if (!clk) 106 return -ENODEV; 107 108 if (clk->ops.mmap) 109 err = clk->ops.mmap(clk, vma); 110 111 put_posix_clock(clk); 112 113 return err; 114 } 115 116 static long posix_clock_ioctl(struct file *fp, 117 unsigned int cmd, unsigned long arg) 118 { 119 struct posix_clock *clk = get_posix_clock(fp); 120 int err = -ENOTTY; 121 122 if (!clk) 123 return -ENODEV; 124 125 if (clk->ops.ioctl) 126 err = clk->ops.ioctl(clk, cmd, arg); 127 128 put_posix_clock(clk); 129 130 return err; 131 } 132 133 #ifdef CONFIG_COMPAT 134 static long posix_clock_compat_ioctl(struct file *fp, 135 unsigned int cmd, unsigned long arg) 136 { 137 struct posix_clock *clk = get_posix_clock(fp); 138 int err = -ENOTTY; 139 140 if (!clk) 141 return -ENODEV; 142 143 if (clk->ops.ioctl) 144 err = clk->ops.ioctl(clk, cmd, arg); 145 146 put_posix_clock(clk); 147 148 return err; 149 } 150 #endif 151 152 static int posix_clock_open(struct inode *inode, struct file *fp) 153 { 154 int err; 155 struct posix_clock *clk = 156 container_of(inode->i_cdev, struct posix_clock, cdev); 157 158 down_read(&clk->rwsem); 159 160 if (clk->zombie) { 161 err = -ENODEV; 162 goto out; 163 } 164 if (clk->ops.open) 165 err = clk->ops.open(clk, fp->f_mode); 166 else 167 err = 0; 168 169 if (!err) { 170 kref_get(&clk->kref); 171 fp->private_data = clk; 172 } 173 out: 174 up_read(&clk->rwsem); 175 return err; 176 } 177 178 static int posix_clock_release(struct inode *inode, struct file *fp) 179 { 180 struct posix_clock *clk = fp->private_data; 181 int err = 0; 182 183 if (clk->ops.release) 184 err = clk->ops.release(clk); 185 186 kref_put(&clk->kref, delete_clock); 187 188 fp->private_data = NULL; 189 190 return err; 191 } 192 193 static const struct file_operations posix_clock_file_operations = { 194 .owner = THIS_MODULE, 195 .llseek = no_llseek, 196 .read = posix_clock_read, 197 .poll = posix_clock_poll, 198 .unlocked_ioctl = posix_clock_ioctl, 199 .open = posix_clock_open, 200 .release = posix_clock_release, 201 .fasync = posix_clock_fasync, 202 .mmap = posix_clock_mmap, 203 #ifdef CONFIG_COMPAT 204 .compat_ioctl = posix_clock_compat_ioctl, 205 #endif 206 }; 207 208 int posix_clock_register(struct posix_clock *clk, dev_t devid) 209 { 210 int err; 211 212 kref_init(&clk->kref); 213 init_rwsem(&clk->rwsem); 214 215 cdev_init(&clk->cdev, &posix_clock_file_operations); 216 clk->cdev.owner = clk->ops.owner; 217 err = cdev_add(&clk->cdev, devid, 1); 218 219 return err; 220 } 221 EXPORT_SYMBOL_GPL(posix_clock_register); 222 223 static void delete_clock(struct kref *kref) 224 { 225 struct posix_clock *clk = container_of(kref, struct posix_clock, kref); 226 227 if (clk->release) 228 clk->release(clk); 229 } 230 231 void posix_clock_unregister(struct posix_clock *clk) 232 { 233 cdev_del(&clk->cdev); 234 235 down_write(&clk->rwsem); 236 clk->zombie = true; 237 up_write(&clk->rwsem); 238 239 kref_put(&clk->kref, delete_clock); 240 } 241 EXPORT_SYMBOL_GPL(posix_clock_unregister); 242 243 struct posix_clock_desc { 244 struct file *fp; 245 struct posix_clock *clk; 246 }; 247 248 static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd) 249 { 250 struct file *fp = fget(CLOCKID_TO_FD(id)); 251 int err = -EINVAL; 252 253 if (!fp) 254 return err; 255 256 if (fp->f_op->open != posix_clock_open || !fp->private_data) 257 goto out; 258 259 cd->fp = fp; 260 cd->clk = get_posix_clock(fp); 261 262 err = cd->clk ? 0 : -ENODEV; 263 out: 264 if (err) 265 fput(fp); 266 return err; 267 } 268 269 static void put_clock_desc(struct posix_clock_desc *cd) 270 { 271 put_posix_clock(cd->clk); 272 fput(cd->fp); 273 } 274 275 static int pc_clock_adjtime(clockid_t id, struct timex *tx) 276 { 277 struct posix_clock_desc cd; 278 int err; 279 280 err = get_clock_desc(id, &cd); 281 if (err) 282 return err; 283 284 if ((cd.fp->f_mode & FMODE_WRITE) == 0) { 285 err = -EACCES; 286 goto out; 287 } 288 289 if (cd.clk->ops.clock_adjtime) 290 err = cd.clk->ops.clock_adjtime(cd.clk, tx); 291 else 292 err = -EOPNOTSUPP; 293 out: 294 put_clock_desc(&cd); 295 296 return err; 297 } 298 299 static int pc_clock_gettime(clockid_t id, struct timespec *ts) 300 { 301 struct posix_clock_desc cd; 302 int err; 303 304 err = get_clock_desc(id, &cd); 305 if (err) 306 return err; 307 308 if (cd.clk->ops.clock_gettime) 309 err = cd.clk->ops.clock_gettime(cd.clk, ts); 310 else 311 err = -EOPNOTSUPP; 312 313 put_clock_desc(&cd); 314 315 return err; 316 } 317 318 static int pc_clock_getres(clockid_t id, struct timespec *ts) 319 { 320 struct posix_clock_desc cd; 321 int err; 322 323 err = get_clock_desc(id, &cd); 324 if (err) 325 return err; 326 327 if (cd.clk->ops.clock_getres) 328 err = cd.clk->ops.clock_getres(cd.clk, ts); 329 else 330 err = -EOPNOTSUPP; 331 332 put_clock_desc(&cd); 333 334 return err; 335 } 336 337 static int pc_clock_settime(clockid_t id, const struct timespec *ts) 338 { 339 struct posix_clock_desc cd; 340 int err; 341 342 err = get_clock_desc(id, &cd); 343 if (err) 344 return err; 345 346 if ((cd.fp->f_mode & FMODE_WRITE) == 0) { 347 err = -EACCES; 348 goto out; 349 } 350 351 if (cd.clk->ops.clock_settime) 352 err = cd.clk->ops.clock_settime(cd.clk, ts); 353 else 354 err = -EOPNOTSUPP; 355 out: 356 put_clock_desc(&cd); 357 358 return err; 359 } 360 361 static int pc_timer_create(struct k_itimer *kit) 362 { 363 clockid_t id = kit->it_clock; 364 struct posix_clock_desc cd; 365 int err; 366 367 err = get_clock_desc(id, &cd); 368 if (err) 369 return err; 370 371 if (cd.clk->ops.timer_create) 372 err = cd.clk->ops.timer_create(cd.clk, kit); 373 else 374 err = -EOPNOTSUPP; 375 376 put_clock_desc(&cd); 377 378 return err; 379 } 380 381 static int pc_timer_delete(struct k_itimer *kit) 382 { 383 clockid_t id = kit->it_clock; 384 struct posix_clock_desc cd; 385 int err; 386 387 err = get_clock_desc(id, &cd); 388 if (err) 389 return err; 390 391 if (cd.clk->ops.timer_delete) 392 err = cd.clk->ops.timer_delete(cd.clk, kit); 393 else 394 err = -EOPNOTSUPP; 395 396 put_clock_desc(&cd); 397 398 return err; 399 } 400 401 static void pc_timer_gettime(struct k_itimer *kit, struct itimerspec *ts) 402 { 403 clockid_t id = kit->it_clock; 404 struct posix_clock_desc cd; 405 406 if (get_clock_desc(id, &cd)) 407 return; 408 409 if (cd.clk->ops.timer_gettime) 410 cd.clk->ops.timer_gettime(cd.clk, kit, ts); 411 412 put_clock_desc(&cd); 413 } 414 415 static int pc_timer_settime(struct k_itimer *kit, int flags, 416 struct itimerspec *ts, struct itimerspec *old) 417 { 418 clockid_t id = kit->it_clock; 419 struct posix_clock_desc cd; 420 int err; 421 422 err = get_clock_desc(id, &cd); 423 if (err) 424 return err; 425 426 if (cd.clk->ops.timer_settime) 427 err = cd.clk->ops.timer_settime(cd.clk, kit, flags, ts, old); 428 else 429 err = -EOPNOTSUPP; 430 431 put_clock_desc(&cd); 432 433 return err; 434 } 435 436 struct k_clock clock_posix_dynamic = { 437 .clock_getres = pc_clock_getres, 438 .clock_set = pc_clock_settime, 439 .clock_get = pc_clock_gettime, 440 .clock_adj = pc_clock_adjtime, 441 .timer_create = pc_timer_create, 442 .timer_set = pc_timer_settime, 443 .timer_del = pc_timer_delete, 444 .timer_get = pc_timer_gettime, 445 }; 446