Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1/*
  2 * PTP 1588 clock support - User space test program
  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 <errno.h>
 21#include <fcntl.h>
 22#include <math.h>
 23#include <signal.h>
 24#include <stdio.h>
 25#include <stdlib.h>
 26#include <string.h>
 27#include <sys/ioctl.h>
 28#include <sys/mman.h>
 29#include <sys/stat.h>
 30#include <sys/time.h>
 31#include <sys/timex.h>
 32#include <sys/types.h>
 33#include <time.h>
 34#include <unistd.h>
 35
 36#include <linux/ptp_clock.h>
 37
 38#define DEVICE "/dev/ptp0"
 39
 40#ifndef ADJ_SETOFFSET
 41#define ADJ_SETOFFSET 0x0100
 42#endif
 43
 44#ifndef CLOCK_INVALID
 45#define CLOCK_INVALID -1
 46#endif
 47
 48/* When glibc offers the syscall, this will go away. */
 49#include <sys/syscall.h>
 50static int clock_adjtime(clockid_t id, struct timex *tx)
 51{
 52	return syscall(__NR_clock_adjtime, id, tx);
 53}
 54
 55static clockid_t get_clockid(int fd)
 56{
 57#define CLOCKFD 3
 58#define FD_TO_CLOCKID(fd)	((~(clockid_t) (fd) << 3) | CLOCKFD)
 59
 60	return FD_TO_CLOCKID(fd);
 61}
 62
 63static void handle_alarm(int s)
 64{
 65	printf("received signal %d\n", s);
 66}
 67
 68static int install_handler(int signum, void (*handler)(int))
 69{
 70	struct sigaction action;
 71	sigset_t mask;
 72
 73	/* Unblock the signal. */
 74	sigemptyset(&mask);
 75	sigaddset(&mask, signum);
 76	sigprocmask(SIG_UNBLOCK, &mask, NULL);
 77
 78	/* Install the signal handler. */
 79	action.sa_handler = handler;
 80	action.sa_flags = 0;
 81	sigemptyset(&action.sa_mask);
 82	sigaction(signum, &action, NULL);
 83
 84	return 0;
 85}
 86
 87static long ppb_to_scaled_ppm(int ppb)
 88{
 89	/*
 90	 * The 'freq' field in the 'struct timex' is in parts per
 91	 * million, but with a 16 bit binary fractional field.
 92	 * Instead of calculating either one of
 93	 *
 94	 *    scaled_ppm = (ppb / 1000) << 16  [1]
 95	 *    scaled_ppm = (ppb << 16) / 1000  [2]
 96	 *
 97	 * we simply use double precision math, in order to avoid the
 98	 * truncation in [1] and the possible overflow in [2].
 99	 */
100	return (long) (ppb * 65.536);
101}
102
103static void usage(char *progname)
104{
105	fprintf(stderr,
106		"usage: %s [options]\n"
107		" -a val     request a one-shot alarm after 'val' seconds\n"
108		" -A val     request a periodic alarm every 'val' seconds\n"
109		" -c         query the ptp clock's capabilities\n"
110		" -d name    device to open\n"
111		" -e val     read 'val' external time stamp events\n"
112		" -f val     adjust the ptp clock frequency by 'val' ppb\n"
113		" -g         get the ptp clock time\n"
114		" -h         prints this message\n"
115		" -p val     enable output with a period of 'val' nanoseconds\n"
116		" -P val     enable or disable (val=1|0) the system clock PPS\n"
117		" -s         set the ptp clock time from the system time\n"
118		" -S         set the system time from the ptp clock time\n"
119		" -t val     shift the ptp clock time by 'val' seconds\n",
120		progname);
121}
122
123int main(int argc, char *argv[])
124{
125	struct ptp_clock_caps caps;
126	struct ptp_extts_event event;
127	struct ptp_extts_request extts_request;
128	struct ptp_perout_request perout_request;
129	struct timespec ts;
130	struct timex tx;
131
132	static timer_t timerid;
133	struct itimerspec timeout;
134	struct sigevent sigevent;
135
136	char *progname;
137	int c, cnt, fd;
138
139	char *device = DEVICE;
140	clockid_t clkid;
141	int adjfreq = 0x7fffffff;
142	int adjtime = 0;
143	int capabilities = 0;
144	int extts = 0;
145	int gettime = 0;
146	int oneshot = 0;
147	int periodic = 0;
148	int perout = -1;
149	int pps = -1;
150	int settime = 0;
151
152	progname = strrchr(argv[0], '/');
153	progname = progname ? 1+progname : argv[0];
154	while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghp:P:sSt:v"))) {
155		switch (c) {
156		case 'a':
157			oneshot = atoi(optarg);
158			break;
159		case 'A':
160			periodic = atoi(optarg);
161			break;
162		case 'c':
163			capabilities = 1;
164			break;
165		case 'd':
166			device = optarg;
167			break;
168		case 'e':
169			extts = atoi(optarg);
170			break;
171		case 'f':
172			adjfreq = atoi(optarg);
173			break;
174		case 'g':
175			gettime = 1;
176			break;
177		case 'p':
178			perout = atoi(optarg);
179			break;
180		case 'P':
181			pps = atoi(optarg);
182			break;
183		case 's':
184			settime = 1;
185			break;
186		case 'S':
187			settime = 2;
188			break;
189		case 't':
190			adjtime = atoi(optarg);
191			break;
192		case 'h':
193			usage(progname);
194			return 0;
195		case '?':
196		default:
197			usage(progname);
198			return -1;
199		}
200	}
201
202	fd = open(device, O_RDWR);
203	if (fd < 0) {
204		fprintf(stderr, "opening %s: %s\n", device, strerror(errno));
205		return -1;
206	}
207
208	clkid = get_clockid(fd);
209	if (CLOCK_INVALID == clkid) {
210		fprintf(stderr, "failed to read clock id\n");
211		return -1;
212	}
213
214	if (capabilities) {
215		if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
216			perror("PTP_CLOCK_GETCAPS");
217		} else {
218			printf("capabilities:\n"
219			       "  %d maximum frequency adjustment (ppb)\n"
220			       "  %d programmable alarms\n"
221			       "  %d external time stamp channels\n"
222			       "  %d programmable periodic signals\n"
223			       "  %d pulse per second\n",
224			       caps.max_adj,
225			       caps.n_alarm,
226			       caps.n_ext_ts,
227			       caps.n_per_out,
228			       caps.pps);
229		}
230	}
231
232	if (0x7fffffff != adjfreq) {
233		memset(&tx, 0, sizeof(tx));
234		tx.modes = ADJ_FREQUENCY;
235		tx.freq = ppb_to_scaled_ppm(adjfreq);
236		if (clock_adjtime(clkid, &tx)) {
237			perror("clock_adjtime");
238		} else {
239			puts("frequency adjustment okay");
240		}
241	}
242
243	if (adjtime) {
244		memset(&tx, 0, sizeof(tx));
245		tx.modes = ADJ_SETOFFSET;
246		tx.time.tv_sec = adjtime;
247		tx.time.tv_usec = 0;
248		if (clock_adjtime(clkid, &tx) < 0) {
249			perror("clock_adjtime");
250		} else {
251			puts("time shift okay");
252		}
253	}
254
255	if (gettime) {
256		if (clock_gettime(clkid, &ts)) {
257			perror("clock_gettime");
258		} else {
259			printf("clock time: %ld.%09ld or %s",
260			       ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
261		}
262	}
263
264	if (settime == 1) {
265		clock_gettime(CLOCK_REALTIME, &ts);
266		if (clock_settime(clkid, &ts)) {
267			perror("clock_settime");
268		} else {
269			puts("set time okay");
270		}
271	}
272
273	if (settime == 2) {
274		clock_gettime(clkid, &ts);
275		if (clock_settime(CLOCK_REALTIME, &ts)) {
276			perror("clock_settime");
277		} else {
278			puts("set time okay");
279		}
280	}
281
282	if (extts) {
283		memset(&extts_request, 0, sizeof(extts_request));
284		extts_request.index = 0;
285		extts_request.flags = PTP_ENABLE_FEATURE;
286		if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
287			perror("PTP_EXTTS_REQUEST");
288			extts = 0;
289		} else {
290			puts("external time stamp request okay");
291		}
292		for (; extts; extts--) {
293			cnt = read(fd, &event, sizeof(event));
294			if (cnt != sizeof(event)) {
295				perror("read");
296				break;
297			}
298			printf("event index %u at %lld.%09u\n", event.index,
299			       event.t.sec, event.t.nsec);
300			fflush(stdout);
301		}
302		/* Disable the feature again. */
303		extts_request.flags = 0;
304		if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
305			perror("PTP_EXTTS_REQUEST");
306		}
307	}
308
309	if (oneshot) {
310		install_handler(SIGALRM, handle_alarm);
311		/* Create a timer. */
312		sigevent.sigev_notify = SIGEV_SIGNAL;
313		sigevent.sigev_signo = SIGALRM;
314		if (timer_create(clkid, &sigevent, &timerid)) {
315			perror("timer_create");
316			return -1;
317		}
318		/* Start the timer. */
319		memset(&timeout, 0, sizeof(timeout));
320		timeout.it_value.tv_sec = oneshot;
321		if (timer_settime(timerid, 0, &timeout, NULL)) {
322			perror("timer_settime");
323			return -1;
324		}
325		pause();
326		timer_delete(timerid);
327	}
328
329	if (periodic) {
330		install_handler(SIGALRM, handle_alarm);
331		/* Create a timer. */
332		sigevent.sigev_notify = SIGEV_SIGNAL;
333		sigevent.sigev_signo = SIGALRM;
334		if (timer_create(clkid, &sigevent, &timerid)) {
335			perror("timer_create");
336			return -1;
337		}
338		/* Start the timer. */
339		memset(&timeout, 0, sizeof(timeout));
340		timeout.it_interval.tv_sec = periodic;
341		timeout.it_value.tv_sec = periodic;
342		if (timer_settime(timerid, 0, &timeout, NULL)) {
343			perror("timer_settime");
344			return -1;
345		}
346		while (1) {
347			pause();
348		}
349		timer_delete(timerid);
350	}
351
352	if (perout >= 0) {
353		if (clock_gettime(clkid, &ts)) {
354			perror("clock_gettime");
355			return -1;
356		}
357		memset(&perout_request, 0, sizeof(perout_request));
358		perout_request.index = 0;
359		perout_request.start.sec = ts.tv_sec + 2;
360		perout_request.start.nsec = 0;
361		perout_request.period.sec = 0;
362		perout_request.period.nsec = perout;
363		if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
364			perror("PTP_PEROUT_REQUEST");
365		} else {
366			puts("periodic output request okay");
367		}
368	}
369
370	if (pps != -1) {
371		int enable = pps ? 1 : 0;
372		if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
373			perror("PTP_ENABLE_PPS");
374		} else {
375			puts("pps for system time request okay");
376		}
377	}
378
379	close(fd);
380	return 0;
381}