Blame view

drivers/ptp/ptp_chardev.c 8.53 KB
d94ba80eb   Richard Cochran   ptp: Added a bran...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  /*
   * PTP 1588 clock support - character device implementation.
   *
   * Copyright (C) 2010 OMICRON electronics GmbH
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or
   *  (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program; if not, write to the Free Software
   *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  #include <linux/module.h>
  #include <linux/posix-clock.h>
  #include <linux/poll.h>
  #include <linux/sched.h>
c7ec0badc   Richard Cochran   ptp: reduce stack...
24
  #include <linux/slab.h>
719f1aa4a   Christopher S. Hall   ptp: Add PTP_SYS_...
25
  #include <linux/timekeeping.h>
d94ba80eb   Richard Cochran   ptp: Added a bran...
26
27
  
  #include "ptp_private.h"
6092315df   Richard Cochran   ptp: introduce pr...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  static int ptp_disable_pinfunc(struct ptp_clock_info *ops,
  			       enum ptp_pin_function func, unsigned int chan)
  {
  	struct ptp_clock_request rq;
  	int err = 0;
  
  	memset(&rq, 0, sizeof(rq));
  
  	switch (func) {
  	case PTP_PF_NONE:
  		break;
  	case PTP_PF_EXTTS:
  		rq.type = PTP_CLK_REQ_EXTTS;
  		rq.extts.index = chan;
  		err = ops->enable(ops, &rq, 0);
  		break;
  	case PTP_PF_PEROUT:
  		rq.type = PTP_CLK_REQ_PEROUT;
  		rq.perout.index = chan;
  		err = ops->enable(ops, &rq, 0);
  		break;
  	case PTP_PF_PHYSYNC:
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	return err;
  }
  
  int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
  		    enum ptp_pin_function func, unsigned int chan)
  {
  	struct ptp_clock_info *info = ptp->info;
  	struct ptp_pin_desc *pin1 = NULL, *pin2 = &info->pin_config[pin];
  	unsigned int i;
  
  	/* Check to see if any other pin previously had this function. */
  	for (i = 0; i < info->n_pins; i++) {
  		if (info->pin_config[i].func == func &&
  		    info->pin_config[i].chan == chan) {
  			pin1 = &info->pin_config[i];
  			break;
  		}
  	}
  	if (pin1 && i == pin)
  		return 0;
  
  	/* Check the desired function and channel. */
  	switch (func) {
  	case PTP_PF_NONE:
  		break;
  	case PTP_PF_EXTTS:
  		if (chan >= info->n_ext_ts)
  			return -EINVAL;
  		break;
  	case PTP_PF_PEROUT:
  		if (chan >= info->n_per_out)
  			return -EINVAL;
  		break;
  	case PTP_PF_PHYSYNC:
72df7a724   Stefan Sørensen   ptp: Allow reassi...
89
90
  		if (chan != 0)
  			return -EINVAL;
294dc77bb   Gustavo A. R. Silva   ptp: fix missing ...
91
  		break;
6092315df   Richard Cochran   ptp: introduce pr...
92
93
94
  	default:
  		return -EINVAL;
  	}
6092315df   Richard Cochran   ptp: introduce pr...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  	if (info->verify(info, pin, func, chan)) {
  		pr_err("driver cannot use function %u on pin %u
  ", func, chan);
  		return -EOPNOTSUPP;
  	}
  
  	/* Disable whatever function was previously assigned. */
  	if (pin1) {
  		ptp_disable_pinfunc(info, func, chan);
  		pin1->func = PTP_PF_NONE;
  		pin1->chan = 0;
  	}
  	ptp_disable_pinfunc(info, pin2->func, pin2->chan);
  	pin2->func = func;
  	pin2->chan = chan;
  
  	return 0;
  }
d94ba80eb   Richard Cochran   ptp: Added a bran...
113
114
115
116
117
118
119
120
121
  int ptp_open(struct posix_clock *pc, fmode_t fmode)
  {
  	return 0;
  }
  
  long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
  {
  	struct ptp_clock_caps caps;
  	struct ptp_clock_request req;
c3484c275   Richard Cochran   ptp: reduce stack...
122
  	struct ptp_sys_offset *sysoff = NULL;
719f1aa4a   Christopher S. Hall   ptp: Add PTP_SYS_...
123
  	struct ptp_sys_offset_precise precise_offset;
6092315df   Richard Cochran   ptp: introduce pr...
124
  	struct ptp_pin_desc pd;
d94ba80eb   Richard Cochran   ptp: Added a bran...
125
126
  	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
  	struct ptp_clock_info *ops = ptp->info;
215b13dd2   Richard Cochran   ptp: add an ioctl...
127
  	struct ptp_clock_time *pct;
e13cfcb03   Richard Cochran   ptp: use the 64 b...
128
  	struct timespec64 ts;
719f1aa4a   Christopher S. Hall   ptp: Add PTP_SYS_...
129
  	struct system_device_crosststamp xtstamp;
d94ba80eb   Richard Cochran   ptp: Added a bran...
130
  	int enable, err = 0;
6092315df   Richard Cochran   ptp: introduce pr...
131
  	unsigned int i, pin_index;
d94ba80eb   Richard Cochran   ptp: Added a bran...
132
133
134
135
136
137
138
139
140
141
  
  	switch (cmd) {
  
  	case PTP_CLOCK_GETCAPS:
  		memset(&caps, 0, sizeof(caps));
  		caps.max_adj = ptp->info->max_adj;
  		caps.n_alarm = ptp->info->n_alarm;
  		caps.n_ext_ts = ptp->info->n_ext_ts;
  		caps.n_per_out = ptp->info->n_per_out;
  		caps.pps = ptp->info->pps;
6092315df   Richard Cochran   ptp: introduce pr...
142
  		caps.n_pins = ptp->info->n_pins;
719f1aa4a   Christopher S. Hall   ptp: Add PTP_SYS_...
143
  		caps.cross_timestamping = ptp->info->getcrosststamp != NULL;
e23ef227d   Dan Carpenter   ptp: Return -EFAU...
144
145
  		if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
  			err = -EFAULT;
d94ba80eb   Richard Cochran   ptp: Added a bran...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  		break;
  
  	case PTP_EXTTS_REQUEST:
  		if (copy_from_user(&req.extts, (void __user *)arg,
  				   sizeof(req.extts))) {
  			err = -EFAULT;
  			break;
  		}
  		if (req.extts.index >= ops->n_ext_ts) {
  			err = -EINVAL;
  			break;
  		}
  		req.type = PTP_CLK_REQ_EXTTS;
  		enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0;
  		err = ops->enable(ops, &req, enable);
  		break;
  
  	case PTP_PEROUT_REQUEST:
  		if (copy_from_user(&req.perout, (void __user *)arg,
  				   sizeof(req.perout))) {
  			err = -EFAULT;
  			break;
  		}
  		if (req.perout.index >= ops->n_per_out) {
  			err = -EINVAL;
  			break;
  		}
  		req.type = PTP_CLK_REQ_PEROUT;
  		enable = req.perout.period.sec || req.perout.period.nsec;
  		err = ops->enable(ops, &req, enable);
  		break;
  
  	case PTP_ENABLE_PPS:
  		if (!capable(CAP_SYS_TIME))
  			return -EPERM;
  		req.type = PTP_CLK_REQ_PPS;
  		enable = arg ? 1 : 0;
  		err = ops->enable(ops, &req, enable);
  		break;
719f1aa4a   Christopher S. Hall   ptp: Add PTP_SYS_...
185
186
187
188
189
190
191
192
  	case PTP_SYS_OFFSET_PRECISE:
  		if (!ptp->info->getcrosststamp) {
  			err = -EOPNOTSUPP;
  			break;
  		}
  		err = ptp->info->getcrosststamp(ptp->info, &xtstamp);
  		if (err)
  			break;
02a9079c6   Vlad Tsyrklevich   drivers/ptp: Fix ...
193
  		memset(&precise_offset, 0, sizeof(precise_offset));
719f1aa4a   Christopher S. Hall   ptp: Add PTP_SYS_...
194
195
196
197
198
199
200
201
202
203
204
205
206
  		ts = ktime_to_timespec64(xtstamp.device);
  		precise_offset.device.sec = ts.tv_sec;
  		precise_offset.device.nsec = ts.tv_nsec;
  		ts = ktime_to_timespec64(xtstamp.sys_realtime);
  		precise_offset.sys_realtime.sec = ts.tv_sec;
  		precise_offset.sys_realtime.nsec = ts.tv_nsec;
  		ts = ktime_to_timespec64(xtstamp.sys_monoraw);
  		precise_offset.sys_monoraw.sec = ts.tv_sec;
  		precise_offset.sys_monoraw.nsec = ts.tv_nsec;
  		if (copy_to_user((void __user *)arg, &precise_offset,
  				 sizeof(precise_offset)))
  			err = -EFAULT;
  		break;
215b13dd2   Richard Cochran   ptp: add an ioctl...
207
  	case PTP_SYS_OFFSET:
2ece068e1   Muhammad Falak R Wani   ptp: use memdup_u...
208
209
210
  		sysoff = memdup_user((void __user *)arg, sizeof(*sysoff));
  		if (IS_ERR(sysoff)) {
  			err = PTR_ERR(sysoff);
6756325a9   Dan Carpenter   ptp: oops in ptp_...
211
  			sysoff = NULL;
215b13dd2   Richard Cochran   ptp: add an ioctl...
212
213
  			break;
  		}
c3484c275   Richard Cochran   ptp: reduce stack...
214
  		if (sysoff->n_samples > PTP_MAX_SAMPLES) {
215b13dd2   Richard Cochran   ptp: add an ioctl...
215
216
217
  			err = -EINVAL;
  			break;
  		}
c3484c275   Richard Cochran   ptp: reduce stack...
218
219
  		pct = &sysoff->ts[0];
  		for (i = 0; i < sysoff->n_samples; i++) {
e13cfcb03   Richard Cochran   ptp: use the 64 b...
220
  			getnstimeofday64(&ts);
215b13dd2   Richard Cochran   ptp: add an ioctl...
221
222
223
  			pct->sec = ts.tv_sec;
  			pct->nsec = ts.tv_nsec;
  			pct++;
ed7c6317b   Richard Cochran   ptp: remove 32 bi...
224
  			ptp->info->gettime64(ptp->info, &ts);
215b13dd2   Richard Cochran   ptp: add an ioctl...
225
226
227
228
  			pct->sec = ts.tv_sec;
  			pct->nsec = ts.tv_nsec;
  			pct++;
  		}
e13cfcb03   Richard Cochran   ptp: use the 64 b...
229
  		getnstimeofday64(&ts);
215b13dd2   Richard Cochran   ptp: add an ioctl...
230
231
  		pct->sec = ts.tv_sec;
  		pct->nsec = ts.tv_nsec;
c3484c275   Richard Cochran   ptp: reduce stack...
232
  		if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff)))
215b13dd2   Richard Cochran   ptp: add an ioctl...
233
234
  			err = -EFAULT;
  		break;
6092315df   Richard Cochran   ptp: introduce pr...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  	case PTP_PIN_GETFUNC:
  		if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
  			err = -EFAULT;
  			break;
  		}
  		pin_index = pd.index;
  		if (pin_index >= ops->n_pins) {
  			err = -EINVAL;
  			break;
  		}
  		if (mutex_lock_interruptible(&ptp->pincfg_mux))
  			return -ERESTARTSYS;
  		pd = ops->pin_config[pin_index];
  		mutex_unlock(&ptp->pincfg_mux);
  		if (!err && copy_to_user((void __user *)arg, &pd, sizeof(pd)))
  			err = -EFAULT;
  		break;
  
  	case PTP_PIN_SETFUNC:
  		if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) {
  			err = -EFAULT;
  			break;
  		}
  		pin_index = pd.index;
  		if (pin_index >= ops->n_pins) {
  			err = -EINVAL;
  			break;
  		}
  		if (mutex_lock_interruptible(&ptp->pincfg_mux))
  			return -ERESTARTSYS;
  		err = ptp_set_pinfunc(ptp, pin_index, pd.func, pd.chan);
  		mutex_unlock(&ptp->pincfg_mux);
  		break;
d94ba80eb   Richard Cochran   ptp: Added a bran...
268
269
270
271
  	default:
  		err = -ENOTTY;
  		break;
  	}
c3484c275   Richard Cochran   ptp: reduce stack...
272
273
  
  	kfree(sysoff);
d94ba80eb   Richard Cochran   ptp: Added a bran...
274
275
276
277
278
279
280
281
282
283
284
  	return err;
  }
  
  unsigned int ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
  {
  	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
  
  	poll_wait(fp, &ptp->tsev_wq, wait);
  
  	return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
  }
c7ec0badc   Richard Cochran   ptp: reduce stack...
285
  #define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event))
d94ba80eb   Richard Cochran   ptp: Added a bran...
286
287
288
289
290
  ssize_t ptp_read(struct posix_clock *pc,
  		 uint rdflags, char __user *buf, size_t cnt)
  {
  	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
  	struct timestamp_event_queue *queue = &ptp->tsevq;
c7ec0badc   Richard Cochran   ptp: reduce stack...
291
  	struct ptp_extts_event *event;
d94ba80eb   Richard Cochran   ptp: Added a bran...
292
293
  	unsigned long flags;
  	size_t qcnt, i;
c7ec0badc   Richard Cochran   ptp: reduce stack...
294
  	int result;
d94ba80eb   Richard Cochran   ptp: Added a bran...
295
296
297
  
  	if (cnt % sizeof(struct ptp_extts_event) != 0)
  		return -EINVAL;
c7ec0badc   Richard Cochran   ptp: reduce stack...
298
299
  	if (cnt > EXTTS_BUFSIZE)
  		cnt = EXTTS_BUFSIZE;
d94ba80eb   Richard Cochran   ptp: Added a bran...
300
301
302
303
304
305
306
307
308
309
310
  
  	cnt = cnt / sizeof(struct ptp_extts_event);
  
  	if (mutex_lock_interruptible(&ptp->tsevq_mux))
  		return -ERESTARTSYS;
  
  	if (wait_event_interruptible(ptp->tsev_wq,
  				     ptp->defunct || queue_cnt(queue))) {
  		mutex_unlock(&ptp->tsevq_mux);
  		return -ERESTARTSYS;
  	}
fb5a18cf7   Dan Carpenter   ptp: Fix some loc...
311
312
  	if (ptp->defunct) {
  		mutex_unlock(&ptp->tsevq_mux);
d94ba80eb   Richard Cochran   ptp: Added a bran...
313
  		return -ENODEV;
fb5a18cf7   Dan Carpenter   ptp: Fix some loc...
314
  	}
d94ba80eb   Richard Cochran   ptp: Added a bran...
315

c7ec0badc   Richard Cochran   ptp: reduce stack...
316
317
318
319
320
  	event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL);
  	if (!event) {
  		mutex_unlock(&ptp->tsevq_mux);
  		return -ENOMEM;
  	}
d94ba80eb   Richard Cochran   ptp: Added a bran...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
  	spin_lock_irqsave(&queue->lock, flags);
  
  	qcnt = queue_cnt(queue);
  
  	if (cnt > qcnt)
  		cnt = qcnt;
  
  	for (i = 0; i < cnt; i++) {
  		event[i] = queue->buf[queue->head];
  		queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
  	}
  
  	spin_unlock_irqrestore(&queue->lock, flags);
  
  	cnt = cnt * sizeof(struct ptp_extts_event);
  
  	mutex_unlock(&ptp->tsevq_mux);
c7ec0badc   Richard Cochran   ptp: reduce stack...
338
  	result = cnt;
fb5a18cf7   Dan Carpenter   ptp: Fix some loc...
339
  	if (copy_to_user(buf, event, cnt))
c7ec0badc   Richard Cochran   ptp: reduce stack...
340
  		result = -EFAULT;
d94ba80eb   Richard Cochran   ptp: Added a bran...
341

c7ec0badc   Richard Cochran   ptp: reduce stack...
342
343
  	kfree(event);
  	return result;
d94ba80eb   Richard Cochran   ptp: Added a bran...
344
  }