Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ptp_sysfs.c
Go to the documentation of this file.
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 
24 static 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) \
32 static 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 
39 PTP_SHOW_INT(max_adj);
40 PTP_SHOW_INT(n_alarm);
41 PTP_SHOW_INT(n_ext_ts);
42 PTP_SHOW_INT(n_per_out);
43 PTP_SHOW_INT(pps);
44 
45 #define PTP_RO_ATTR(_var, _name) { \
46  .attr = { .name = __stringify(_name), .mode = 0444 }, \
47  .show = _var##_show, \
48 }
49 
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),
58 };
59 
60 static 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;
81 out:
82  return err;
83 }
84 
85 static 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 
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);
113 out:
114  mutex_unlock(&ptp->tsevq_mux);
115  return cnt;
116 }
117 
118 static 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;
141 out:
142  return err;
143 }
144 
145 static 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;
167 out:
168  return err;
169 }
170 
171 static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store);
172 static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL);
173 static DEVICE_ATTR(period, 0220, NULL, period_store);
174 static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store);
175 
176 int 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 
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;
219 out4:
220  if (info->n_per_out)
221  device_remove_file(dev, &dev_attr_period);
222 out3:
223  if (info->n_ext_ts)
224  device_remove_file(dev, &dev_attr_fifo);
225 out2:
226  if (info->n_ext_ts)
227  device_remove_file(dev, &dev_attr_extts_enable);
228 out1:
229  return err;
230 }