ptp_chardev.c 9.7 KB
Newer Older
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>
24
#include <linux/slab.h>
25
#include <linux/timekeeping.h>
26

27 28
#include <linux/nospec.h>

29 30
#include "ptp_private.h"

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 89 90 91
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:
92 93
		if (chan != 0)
			return -EINVAL;
94
		break;
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
	default:
		return -EINVAL;
	}

	if (info->verify(info, pin, func, chan)) {
		pr_err("driver cannot use function %u on pin %u\n", 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;
}

117 118 119 120 121 122 123 124
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 *ptp = container_of(pc, struct ptp_clock, clock);
125
	struct ptp_sys_offset_extended *extoff = NULL;
126 127
	struct ptp_sys_offset_precise precise_offset;
	struct system_device_crosststamp xtstamp;
128
	struct ptp_clock_info *ops = ptp->info;
129
	struct ptp_sys_offset *sysoff = NULL;
130
	struct ptp_system_timestamp sts;
131 132
	struct ptp_clock_request req;
	struct ptp_clock_caps caps;
133
	struct ptp_clock_time *pct;
134 135
	unsigned int i, pin_index;
	struct ptp_pin_desc pd;
136
	struct timespec64 ts;
137 138 139 140 141 142 143 144 145 146 147
	int enable, err = 0;

	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;
148
		caps.n_pins = ptp->info->n_pins;
149
		caps.cross_timestamping = ptp->info->getcrosststamp != NULL;
150 151
		if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
			err = -EFAULT;
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 185 186 187 188 189 190 191
		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;

192 193 194 195 196 197 198 199 200
	case PTP_SYS_OFFSET_PRECISE:
		if (!ptp->info->getcrosststamp) {
			err = -EOPNOTSUPP;
			break;
		}
		err = ptp->info->getcrosststamp(ptp->info, &xtstamp);
		if (err)
			break;

201
		memset(&precise_offset, 0, sizeof(precise_offset));
202 203 204 205 206 207 208 209 210 211 212 213 214 215
		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;

216 217 218 219 220 221 222 223 224 225 226
	case PTP_SYS_OFFSET_EXTENDED:
		if (!ptp->info->gettimex64) {
			err = -EOPNOTSUPP;
			break;
		}
		extoff = memdup_user((void __user *)arg, sizeof(*extoff));
		if (IS_ERR(extoff)) {
			err = PTR_ERR(extoff);
			extoff = NULL;
			break;
		}
227 228
		if (extoff->n_samples > PTP_MAX_SAMPLES
		    || extoff->rsv[0] || extoff->rsv[1] || extoff->rsv[2]) {
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
			err = -EINVAL;
			break;
		}
		for (i = 0; i < extoff->n_samples; i++) {
			err = ptp->info->gettimex64(ptp->info, &ts, &sts);
			if (err)
				goto out;
			extoff->ts[i][0].sec = sts.pre_ts.tv_sec;
			extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec;
			extoff->ts[i][1].sec = ts.tv_sec;
			extoff->ts[i][1].nsec = ts.tv_nsec;
			extoff->ts[i][2].sec = sts.post_ts.tv_sec;
			extoff->ts[i][2].nsec = sts.post_ts.tv_nsec;
		}
		if (copy_to_user((void __user *)arg, extoff, sizeof(*extoff)))
			err = -EFAULT;
		break;

247
	case PTP_SYS_OFFSET:
M
Muhammad Falak R Wani 已提交
248 249 250
		sysoff = memdup_user((void __user *)arg, sizeof(*sysoff));
		if (IS_ERR(sysoff)) {
			err = PTR_ERR(sysoff);
D
Dan Carpenter 已提交
251
			sysoff = NULL;
252 253
			break;
		}
254
		if (sysoff->n_samples > PTP_MAX_SAMPLES) {
255 256 257
			err = -EINVAL;
			break;
		}
258 259
		pct = &sysoff->ts[0];
		for (i = 0; i < sysoff->n_samples; i++) {
260
			ktime_get_real_ts64(&ts);
261 262 263
			pct->sec = ts.tv_sec;
			pct->nsec = ts.tv_nsec;
			pct++;
264 265 266 267
			if (ops->gettimex64)
				err = ops->gettimex64(ops, &ts, NULL);
			else
				err = ops->gettime64(ops, &ts);
268 269
			if (err)
				goto out;
270 271 272 273
			pct->sec = ts.tv_sec;
			pct->nsec = ts.tv_nsec;
			pct++;
		}
274
		ktime_get_real_ts64(&ts);
275 276
		pct->sec = ts.tv_sec;
		pct->nsec = ts.tv_nsec;
277
		if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff)))
278 279 280
			err = -EFAULT;
		break;

281 282 283 284 285 286 287 288 289 290
	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;
		}
291
		pin_index = array_index_nospec(pin_index, ops->n_pins);
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
		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;
		}
310
		pin_index = array_index_nospec(pin_index, ops->n_pins);
311 312 313 314 315 316
		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;

317 318 319 320
	default:
		err = -ENOTTY;
		break;
	}
321

322
out:
323
	kfree(extoff);
324
	kfree(sysoff);
325 326 327
	return err;
}

328
__poll_t ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
329 330 331 332 333
{
	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);

	poll_wait(fp, &ptp->tsev_wq, wait);

334
	return queue_cnt(&ptp->tsevq) ? EPOLLIN : 0;
335 336
}

337 338
#define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event))

339 340 341 342 343
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;
344
	struct ptp_extts_event *event;
345 346
	unsigned long flags;
	size_t qcnt, i;
347
	int result;
348 349 350 351

	if (cnt % sizeof(struct ptp_extts_event) != 0)
		return -EINVAL;

352 353
	if (cnt > EXTTS_BUFSIZE)
		cnt = EXTTS_BUFSIZE;
354 355 356 357 358 359 360 361 362 363 364 365

	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;
	}

366 367
	if (ptp->defunct) {
		mutex_unlock(&ptp->tsevq_mux);
368
		return -ENODEV;
369
	}
370

371 372 373 374 375 376
	event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL);
	if (!event) {
		mutex_unlock(&ptp->tsevq_mux);
		return -ENOMEM;
	}

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
	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);

395
	result = cnt;
396
	if (copy_to_user(buf, event, cnt))
397
		result = -EFAULT;
398

399 400
	kfree(event);
	return result;
401
}