linux/drivers/ptp/ptp_sysfs.c
<<
>>
Prefs
   1/*
   2 * PTP 1588 clock support - sysfs interface.
   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/capability.h>
  21
  22#include "ptp_private.h"
  23
  24static ssize_t clock_name_show(struct device *dev,
  25                               struct device_attribute *attr, char *page)
  26{
  27        struct ptp_clock *ptp = dev_get_drvdata(dev);
  28        return snprintf(page, PAGE_SIZE-1, "%s\n", ptp->info->name);
  29}
  30
  31#define PTP_SHOW_INT(name)                                              \
  32static ssize_t name##_show(struct device *dev,                          \
  33                           struct device_attribute *attr, char *page)   \
  34{                                                                       \
  35        struct ptp_clock *ptp = dev_get_drvdata(dev);                   \
  36        return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->info->name);    \
  37}
  38
  39PTP_SHOW_INT(max_adj);
  40PTP_SHOW_INT(n_alarm);
  41PTP_SHOW_INT(n_ext_ts);
  42PTP_SHOW_INT(n_per_out);
  43PTP_SHOW_INT(pps);
  44
  45#define PTP_RO_ATTR(_var, _name) {                              \
  46        .attr   = { .name = __stringify(_name), .mode = 0444 }, \
  47        .show   = _var##_show,                                  \
  48}
  49
  50struct device_attribute ptp_dev_attrs[] = {
  51        PTP_RO_ATTR(clock_name, clock_name),
  52        PTP_RO_ATTR(max_adj,    max_adjustment),
  53        PTP_RO_ATTR(n_alarm,    n_alarms),
  54        PTP_RO_ATTR(n_ext_ts,   n_external_timestamps),
  55        PTP_RO_ATTR(n_per_out,  n_periodic_outputs),
  56        PTP_RO_ATTR(pps,        pps_available),
  57        __ATTR_NULL,
  58};
  59
  60static ssize_t extts_enable_store(struct device *dev,
  61                                  struct device_attribute *attr,
  62                                  const char *buf, size_t count)
  63{
  64        struct ptp_clock *ptp = dev_get_drvdata(dev);
  65        struct ptp_clock_info *ops = ptp->info;
  66        struct ptp_clock_request req = { .type = PTP_CLK_REQ_EXTTS };
  67        int cnt, enable;
  68        int err = -EINVAL;
  69
  70        cnt = sscanf(buf, "%u %d", &req.extts.index, &enable);
  71        if (cnt != 2)
  72                goto out;
  73        if (req.extts.index >= ops->n_ext_ts)
  74                goto out;
  75
  76        err = ops->enable(ops, &req, enable ? 1 : 0);
  77        if (err)
  78                goto out;
  79
  80        return count;
  81out:
  82        return err;
  83}
  84
  85static ssize_t extts_fifo_show(struct device *dev,
  86                               struct device_attribute *attr, char *page)
  87{
  88        struct ptp_clock *ptp = dev_get_drvdata(dev);
  89        struct timestamp_event_queue *queue = &ptp->tsevq;
  90        struct ptp_extts_event event;
  91        unsigned long flags;
  92        size_t qcnt;
  93        int cnt = 0;
  94
  95        memset(&event, 0, sizeof(event));
  96
  97        if (mutex_lock_interruptible(&ptp->tsevq_mux))
  98                return -ERESTARTSYS;
  99
 100        spin_lock_irqsave(&queue->lock, flags);
 101        qcnt = queue_cnt(queue);
 102        if (qcnt) {
 103                event = queue->buf[queue->head];
 104                queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
 105        }
 106        spin_unlock_irqrestore(&queue->lock, flags);
 107
 108        if (!qcnt)
 109                goto out;
 110
 111        cnt = snprintf(page, PAGE_SIZE, "%u %lld %u\n",
 112                       event.index, event.t.sec, event.t.nsec);
 113out:
 114        mutex_unlock(&ptp->tsevq_mux);
 115        return cnt;
 116}
 117
 118static ssize_t period_store(struct device *dev,
 119                            struct device_attribute *attr,
 120                            const char *buf, size_t count)
 121{
 122        struct ptp_clock *ptp = dev_get_drvdata(dev);
 123        struct ptp_clock_info *ops = ptp->info;
 124        struct ptp_clock_request req = { .type = PTP_CLK_REQ_PEROUT };
 125        int cnt, enable, err = -EINVAL;
 126
 127        cnt = sscanf(buf, "%u %lld %u %lld %u", &req.perout.index,
 128                     &req.perout.start.sec, &req.perout.start.nsec,
 129                     &req.perout.period.sec, &req.perout.period.nsec);
 130        if (cnt != 5)
 131                goto out;
 132        if (req.perout.index >= ops->n_per_out)
 133                goto out;
 134
 135        enable = req.perout.period.sec || req.perout.period.nsec;
 136        err = ops->enable(ops, &req, enable);
 137        if (err)
 138                goto out;
 139
 140        return count;
 141out:
 142        return err;
 143}
 144
 145static ssize_t pps_enable_store(struct device *dev,
 146                                struct device_attribute *attr,
 147                                const char *buf, size_t count)
 148{
 149        struct ptp_clock *ptp = dev_get_drvdata(dev);
 150        struct ptp_clock_info *ops = ptp->info;
 151        struct ptp_clock_request req = { .type = PTP_CLK_REQ_PPS };
 152        int cnt, enable;
 153        int err = -EINVAL;
 154
 155        if (!capable(CAP_SYS_TIME))
 156                return -EPERM;
 157
 158        cnt = sscanf(buf, "%d", &enable);
 159        if (cnt != 1)
 160                goto out;
 161
 162        err = ops->enable(ops, &req, enable ? 1 : 0);
 163        if (err)
 164                goto out;
 165
 166        return count;
 167out:
 168        return err;
 169}
 170
 171static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store);
 172static DEVICE_ATTR(fifo,         0444, extts_fifo_show, NULL);
 173static DEVICE_ATTR(period,       0220, NULL, period_store);
 174static DEVICE_ATTR(pps_enable,   0220, NULL, pps_enable_store);
 175
 176int ptp_cleanup_sysfs(struct ptp_clock *ptp)
 177{
 178        struct device *dev = ptp->dev;
 179        struct ptp_clock_info *info = ptp->info;
 180
 181        if (info->n_ext_ts) {
 182                device_remove_file(dev, &dev_attr_extts_enable);
 183                device_remove_file(dev, &dev_attr_fifo);
 184        }
 185        if (info->n_per_out)
 186                device_remove_file(dev, &dev_attr_period);
 187
 188        if (info->pps)
 189                device_remove_file(dev, &dev_attr_pps_enable);
 190
 191        return 0;
 192}
 193
 194int ptp_populate_sysfs(struct ptp_clock *ptp)
 195{
 196        struct device *dev = ptp->dev;
 197        struct ptp_clock_info *info = ptp->info;
 198        int err;
 199
 200        if (info->n_ext_ts) {
 201                err = device_create_file(dev, &dev_attr_extts_enable);
 202                if (err)
 203                        goto out1;
 204                err = device_create_file(dev, &dev_attr_fifo);
 205                if (err)
 206                        goto out2;
 207        }
 208        if (info->n_per_out) {
 209                err = device_create_file(dev, &dev_attr_period);
 210                if (err)
 211                        goto out3;
 212        }
 213        if (info->pps) {
 214                err = device_create_file(dev, &dev_attr_pps_enable);
 215                if (err)
 216                        goto out4;
 217        }
 218        return 0;
 219out4:
 220        if (info->n_per_out)
 221                device_remove_file(dev, &dev_attr_period);
 222out3:
 223        if (info->n_ext_ts)
 224                device_remove_file(dev, &dev_attr_fifo);
 225out2:
 226        if (info->n_ext_ts)
 227                device_remove_file(dev, &dev_attr_extts_enable);
 228out1:
 229        return err;
 230}
 231