linux/drivers/ptp/ptp_chardev.c
<<
>>
Prefs
   1/*
   2 * PTP 1588 clock support - character device implementation.
   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/module.h>
  21#include <linux/posix-clock.h>
  22#include <linux/poll.h>
  23#include <linux/sched.h>
  24
  25#include "ptp_private.h"
  26
  27int ptp_open(struct posix_clock *pc, fmode_t fmode)
  28{
  29        return 0;
  30}
  31
  32long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
  33{
  34        struct ptp_clock_caps caps;
  35        struct ptp_clock_request req;
  36        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
  37        struct ptp_clock_info *ops = ptp->info;
  38        int enable, err = 0;
  39
  40        switch (cmd) {
  41
  42        case PTP_CLOCK_GETCAPS:
  43                memset(&caps, 0, sizeof(caps));
  44                caps.max_adj = ptp->info->max_adj;
  45                caps.n_alarm = ptp->info->n_alarm;
  46                caps.n_ext_ts = ptp->info->n_ext_ts;
  47                caps.n_per_out = ptp->info->n_per_out;
  48                caps.pps = ptp->info->pps;
  49                if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
  50                        err = -EFAULT;
  51                break;
  52
  53        case PTP_EXTTS_REQUEST:
  54                if (copy_from_user(&req.extts, (void __user *)arg,
  55                                   sizeof(req.extts))) {
  56                        err = -EFAULT;
  57                        break;
  58                }
  59                if (req.extts.index >= ops->n_ext_ts) {
  60                        err = -EINVAL;
  61                        break;
  62                }
  63                req.type = PTP_CLK_REQ_EXTTS;
  64                enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0;
  65                err = ops->enable(ops, &req, enable);
  66                break;
  67
  68        case PTP_PEROUT_REQUEST:
  69                if (copy_from_user(&req.perout, (void __user *)arg,
  70                                   sizeof(req.perout))) {
  71                        err = -EFAULT;
  72                        break;
  73                }
  74                if (req.perout.index >= ops->n_per_out) {
  75                        err = -EINVAL;
  76                        break;
  77                }
  78                req.type = PTP_CLK_REQ_PEROUT;
  79                enable = req.perout.period.sec || req.perout.period.nsec;
  80                err = ops->enable(ops, &req, enable);
  81                break;
  82
  83        case PTP_ENABLE_PPS:
  84                if (!capable(CAP_SYS_TIME))
  85                        return -EPERM;
  86                req.type = PTP_CLK_REQ_PPS;
  87                enable = arg ? 1 : 0;
  88                err = ops->enable(ops, &req, enable);
  89                break;
  90
  91        default:
  92                err = -ENOTTY;
  93                break;
  94        }
  95        return err;
  96}
  97
  98unsigned int ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
  99{
 100        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
 101
 102        poll_wait(fp, &ptp->tsev_wq, wait);
 103
 104        return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
 105}
 106
 107ssize_t ptp_read(struct posix_clock *pc,
 108                 uint rdflags, char __user *buf, size_t cnt)
 109{
 110        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
 111        struct timestamp_event_queue *queue = &ptp->tsevq;
 112        struct ptp_extts_event event[PTP_BUF_TIMESTAMPS];
 113        unsigned long flags;
 114        size_t qcnt, i;
 115
 116        if (cnt % sizeof(struct ptp_extts_event) != 0)
 117                return -EINVAL;
 118
 119        if (cnt > sizeof(event))
 120                cnt = sizeof(event);
 121
 122        cnt = cnt / sizeof(struct ptp_extts_event);
 123
 124        if (mutex_lock_interruptible(&ptp->tsevq_mux))
 125                return -ERESTARTSYS;
 126
 127        if (wait_event_interruptible(ptp->tsev_wq,
 128                                     ptp->defunct || queue_cnt(queue))) {
 129                mutex_unlock(&ptp->tsevq_mux);
 130                return -ERESTARTSYS;
 131        }
 132
 133        if (ptp->defunct) {
 134                mutex_unlock(&ptp->tsevq_mux);
 135                return -ENODEV;
 136        }
 137
 138        spin_lock_irqsave(&queue->lock, flags);
 139
 140        qcnt = queue_cnt(queue);
 141
 142        if (cnt > qcnt)
 143                cnt = qcnt;
 144
 145        for (i = 0; i < cnt; i++) {
 146                event[i] = queue->buf[queue->head];
 147                queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
 148        }
 149
 150        spin_unlock_irqrestore(&queue->lock, flags);
 151
 152        cnt = cnt * sizeof(struct ptp_extts_event);
 153
 154        mutex_unlock(&ptp->tsevq_mux);
 155
 156        if (copy_to_user(buf, event, cnt))
 157                return -EFAULT;
 158
 159        return cnt;
 160}
 161
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.