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 
25 #include "ptp_private.h"
26 
ptp_open(struct posix_clock * pc,fmode_t fmode)27 int ptp_open(struct posix_clock *pc, fmode_t fmode)
28 {
29 	return 0;
30 }
31 
ptp_ioctl(struct posix_clock * pc,unsigned int cmd,unsigned long arg)32 long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
33 {
34 	struct ptp_clock_caps caps;
35 	struct ptp_clock_request req;
36 	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
37 	struct ptp_clock_info *ops = ptp->info;
38 	int enable, err = 0;
39 
40 	switch (cmd) {
41 
42 	case PTP_CLOCK_GETCAPS:
43 		memset(&caps, 0, sizeof(caps));
44 		caps.max_adj = ptp->info->max_adj;
45 		caps.n_alarm = ptp->info->n_alarm;
46 		caps.n_ext_ts = ptp->info->n_ext_ts;
47 		caps.n_per_out = ptp->info->n_per_out;
48 		caps.pps = ptp->info->pps;
49 		if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
50 			err = -EFAULT;
51 		break;
52 
53 	case PTP_EXTTS_REQUEST:
54 		if (copy_from_user(&req.extts, (void __user *)arg,
55 				   sizeof(req.extts))) {
56 			err = -EFAULT;
57 			break;
58 		}
59 		if (req.extts.index >= ops->n_ext_ts) {
60 			err = -EINVAL;
61 			break;
62 		}
63 		req.type = PTP_CLK_REQ_EXTTS;
64 		enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0;
65 		err = ops->enable(ops, &req, enable);
66 		break;
67 
68 	case PTP_PEROUT_REQUEST:
69 		if (copy_from_user(&req.perout, (void __user *)arg,
70 				   sizeof(req.perout))) {
71 			err = -EFAULT;
72 			break;
73 		}
74 		if (req.perout.index >= ops->n_per_out) {
75 			err = -EINVAL;
76 			break;
77 		}
78 		req.type = PTP_CLK_REQ_PEROUT;
79 		enable = req.perout.period.sec || req.perout.period.nsec;
80 		err = ops->enable(ops, &req, enable);
81 		break;
82 
83 	case PTP_ENABLE_PPS:
84 		if (!capable(CAP_SYS_TIME))
85 			return -EPERM;
86 		req.type = PTP_CLK_REQ_PPS;
87 		enable = arg ? 1 : 0;
88 		err = ops->enable(ops, &req, enable);
89 		break;
90 
91 	default:
92 		err = -ENOTTY;
93 		break;
94 	}
95 	return err;
96 }
97 
ptp_poll(struct posix_clock * pc,struct file * fp,poll_table * wait)98 unsigned int ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
99 {
100 	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
101 
102 	poll_wait(fp, &ptp->tsev_wq, wait);
103 
104 	return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
105 }
106 
ptp_read(struct posix_clock * pc,uint rdflags,char __user * buf,size_t cnt)107 ssize_t ptp_read(struct posix_clock *pc,
108 		 uint rdflags, char __user *buf, size_t cnt)
109 {
110 	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
111 	struct timestamp_event_queue *queue = &ptp->tsevq;
112 	struct ptp_extts_event event[PTP_BUF_TIMESTAMPS];
113 	unsigned long flags;
114 	size_t qcnt, i;
115 
116 	if (cnt % sizeof(struct ptp_extts_event) != 0)
117 		return -EINVAL;
118 
119 	if (cnt > sizeof(event))
120 		cnt = sizeof(event);
121 
122 	cnt = cnt / sizeof(struct ptp_extts_event);
123 
124 	if (mutex_lock_interruptible(&ptp->tsevq_mux))
125 		return -ERESTARTSYS;
126 
127 	if (wait_event_interruptible(ptp->tsev_wq,
128 				     ptp->defunct || queue_cnt(queue))) {
129 		mutex_unlock(&ptp->tsevq_mux);
130 		return -ERESTARTSYS;
131 	}
132 
133 	if (ptp->defunct) {
134 		mutex_unlock(&ptp->tsevq_mux);
135 		return -ENODEV;
136 	}
137 
138 	spin_lock_irqsave(&queue->lock, flags);
139 
140 	qcnt = queue_cnt(queue);
141 
142 	if (cnt > qcnt)
143 		cnt = qcnt;
144 
145 	for (i = 0; i < cnt; i++) {
146 		event[i] = queue->buf[queue->head];
147 		queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
148 	}
149 
150 	spin_unlock_irqrestore(&queue->lock, flags);
151 
152 	cnt = cnt * sizeof(struct ptp_extts_event);
153 
154 	mutex_unlock(&ptp->tsevq_mux);
155 
156 	if (copy_to_user(buf, event, cnt))
157 		return -EFAULT;
158 
159 	return cnt;
160 }
161