xref: /linux/drivers/ptp/ptp_chardev.c (revision b889fcf63cb62e7fdb7816565e28f44dbe4a76a5)
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 
28 int ptp_open(struct posix_clock *pc, fmode_t fmode)
29 {
30 	return 0;
31 }
32 
33 long 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 
138 unsigned 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 
149 ssize_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