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#include <linux/slab.h>
  25
  26#include "ptp_private.h"
  27
  28int ptp_open(struct posix_clock *pc, fmode_t fmode)
  29{
  30        return 0;
  31}
  32
  33long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
  34{
  35        struct ptp_clock_caps caps;
  36        struct ptp_clock_request req;
  37        struct ptp_sys_offset *sysoff = NULL;
  38        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
  39        struct ptp_clock_info *ops = ptp->info;
  40        struct ptp_clock_time *pct;
  41        struct timespec ts;
  42        int enable, err = 0;
  43        unsigned int i;
  44
  45        switch (cmd) {
  46
  47        case PTP_CLOCK_GETCAPS:
  48                memset(&caps, 0, sizeof(caps));
  49                caps.max_adj = ptp->info->max_adj;
  50                caps.n_alarm = ptp->info->n_alarm;
  51                caps.n_ext_ts = ptp->info->n_ext_ts;
  52                caps.n_per_out = ptp->info->n_per_out;
  53                caps.pps = ptp->info->pps;
  54                if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
  55                        err = -EFAULT;
  56                break;
  57
  58        case PTP_EXTTS_REQUEST:
  59                if (copy_from_user(&req.extts, (void __user *)arg,
  60                                   sizeof(req.extts))) {
  61                        err = -EFAULT;
  62                        break;
  63                }
  64                if (req.extts.index >= ops->n_ext_ts) {
  65                        err = -EINVAL;
  66                        break;
  67                }
  68                req.type = PTP_CLK_REQ_EXTTS;
  69                enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0;
  70                err = ops->enable(ops, &req, enable);
  71                break;
  72
  73        case PTP_PEROUT_REQUEST:
  74                if (copy_from_user(&req.perout, (void __user *)arg,
  75                                   sizeof(req.perout))) {
  76                        err = -EFAULT;
  77                        break;
  78                }
  79                if (req.perout.index >= ops->n_per_out) {
  80                        err = -EINVAL;
  81                        break;
  82                }
  83                req.type = PTP_CLK_REQ_PEROUT;
  84                enable = req.perout.period.sec || req.perout.period.nsec;
  85                err = ops->enable(ops, &req, enable);
  86                break;
  87
  88        case PTP_ENABLE_PPS:
  89                if (!capable(CAP_SYS_TIME))
  90                        return -EPERM;
  91                req.type = PTP_CLK_REQ_PPS;
  92                enable = arg ? 1 : 0;
  93                err = ops->enable(ops, &req, enable);
  94                break;
  95
  96        case PTP_SYS_OFFSET:
  97                sysoff = kmalloc(sizeof(*sysoff), GFP_KERNEL);
  98                if (!sysoff) {
  99                        err = -ENOMEM;
 100                        break;
 101                }
 102                if (copy_from_user(sysoff, (void __user *)arg,
 103                                   sizeof(*sysoff))) {
 104                        err = -EFAULT;
 105                        break;
 106                }
 107                if (sysoff->n_samples > PTP_MAX_SAMPLES) {
 108                        err = -EINVAL;
 109                        break;
 110                }
 111                pct = &sysoff->ts[0];
 112                for (i = 0; i < sysoff->n_samples; i++) {
 113                        getnstimeofday(&ts);
 114                        pct->sec = ts.tv_sec;
 115                        pct->nsec = ts.tv_nsec;
 116                        pct++;
 117                        ptp->info->gettime(ptp->info, &ts);
 118                        pct->sec = ts.tv_sec;
 119                        pct->nsec = ts.tv_nsec;
 120                        pct++;
 121                }
 122                getnstimeofday(&ts);
 123                pct->sec = ts.tv_sec;
 124                pct->nsec = ts.tv_nsec;
 125                if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff)))
 126                        err = -EFAULT;
 127                break;
 128
 129        default:
 130                err = -ENOTTY;
 131                break;
 132        }
 133
 134        kfree(sysoff);
 135        return err;
 136}
 137
 138unsigned int ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
 139{
 140        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
 141
 142        poll_wait(fp, &ptp->tsev_wq, wait);
 143
 144        return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
 145}
 146
 147#define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event))
 148
 149ssize_t ptp_read(struct posix_clock *pc,
 150                 uint rdflags, char __user *buf, size_t cnt)
 151{
 152        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
 153        struct timestamp_event_queue *queue = &ptp->tsevq;
 154        struct ptp_extts_event *event;
 155        unsigned long flags;
 156        size_t qcnt, i;
 157        int result;
 158
 159        if (cnt % sizeof(struct ptp_extts_event) != 0)
 160                return -EINVAL;
 161
 162        if (cnt > EXTTS_BUFSIZE)
 163                cnt = EXTTS_BUFSIZE;
 164
 165        cnt = cnt / sizeof(struct ptp_extts_event);
 166
 167        if (mutex_lock_interruptible(&ptp->tsevq_mux))
 168                return -ERESTARTSYS;
 169
 170        if (wait_event_interruptible(ptp->tsev_wq,
 171                                     ptp->defunct || queue_cnt(queue))) {
 172                mutex_unlock(&ptp->tsevq_mux);
 173                return -ERESTARTSYS;
 174        }
 175
 176        if (ptp->defunct) {
 177                mutex_unlock(&ptp->tsevq_mux);
 178                return -ENODEV;
 179        }
 180
 181        event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL);
 182        if (!event) {
 183                mutex_unlock(&ptp->tsevq_mux);
 184                return -ENOMEM;
 185        }
 186
 187        spin_lock_irqsave(&queue->lock, flags);
 188
 189        qcnt = queue_cnt(queue);
 190
 191        if (cnt > qcnt)
 192                cnt = qcnt;
 193
 194        for (i = 0; i < cnt; i++) {
 195                event[i] = queue->buf[queue->head];
 196                queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
 197        }
 198
 199        spin_unlock_irqrestore(&queue->lock, flags);
 200
 201        cnt = cnt * sizeof(struct ptp_extts_event);
 202
 203        mutex_unlock(&ptp->tsevq_mux);
 204
 205        result = cnt;
 206        if (copy_to_user(buf, event, cnt))
 207                result = -EFAULT;
 208
 209        kfree(event);
 210        return result;
 211}
 212