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