brintos

brintos / linux-shallow public Read only

0
0
Text · 14.9 KiB · 011252f Raw
627 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * PTP 1588 clock support - User space test program4 *5 * Copyright (C) 2010 OMICRON electronics GmbH6 */7#define _GNU_SOURCE8#define __SANE_USERSPACE_TYPES__        /* For PPC64, to get LL64 types */9#include <errno.h>10#include <fcntl.h>11#include <inttypes.h>12#include <math.h>13#include <signal.h>14#include <stdio.h>15#include <stdlib.h>16#include <string.h>17#include <sys/ioctl.h>18#include <sys/mman.h>19#include <sys/stat.h>20#include <sys/time.h>21#include <sys/timex.h>22#include <sys/types.h>23#include <time.h>24#include <unistd.h>25 26#include <linux/ptp_clock.h>27 28#define DEVICE "/dev/ptp0"29 30#ifndef ADJ_SETOFFSET31#define ADJ_SETOFFSET 0x010032#endif33 34#ifndef CLOCK_INVALID35#define CLOCK_INVALID -136#endif37 38#define NSEC_PER_SEC 1000000000LL39 40/* clock_adjtime is not available in GLIBC < 2.14 */41#if !__GLIBC_PREREQ(2, 14)42#include <sys/syscall.h>43static int clock_adjtime(clockid_t id, struct timex *tx)44{45	return syscall(__NR_clock_adjtime, id, tx);46}47#endif48 49static void show_flag_test(int rq_index, unsigned int flags, int err)50{51	printf("PTP_EXTTS_REQUEST%c flags 0x%08x : (%d) %s\n",52	       rq_index ? '1' + rq_index : ' ',53	       flags, err, strerror(errno));54	/* sigh, uClibc ... */55	errno = 0;56}57 58static void do_flag_test(int fd, unsigned int index)59{60	struct ptp_extts_request extts_request;61	unsigned long request[2] = {62		PTP_EXTTS_REQUEST,63		PTP_EXTTS_REQUEST2,64	};65	unsigned int enable_flags[5] = {66		PTP_ENABLE_FEATURE,67		PTP_ENABLE_FEATURE | PTP_RISING_EDGE,68		PTP_ENABLE_FEATURE | PTP_FALLING_EDGE,69		PTP_ENABLE_FEATURE | PTP_RISING_EDGE | PTP_FALLING_EDGE,70		PTP_ENABLE_FEATURE | (PTP_EXTTS_VALID_FLAGS + 1),71	};72	int err, i, j;73 74	memset(&extts_request, 0, sizeof(extts_request));75	extts_request.index = index;76 77	for (i = 0; i < 2; i++) {78		for (j = 0; j < 5; j++) {79			extts_request.flags = enable_flags[j];80			err = ioctl(fd, request[i], &extts_request);81			show_flag_test(i, extts_request.flags, err);82 83			extts_request.flags = 0;84			err = ioctl(fd, request[i], &extts_request);85		}86	}87}88 89static clockid_t get_clockid(int fd)90{91#define CLOCKFD 392	return (((unsigned int) ~fd) << 3) | CLOCKFD;93}94 95static long ppb_to_scaled_ppm(int ppb)96{97	/*98	 * The 'freq' field in the 'struct timex' is in parts per99	 * million, but with a 16 bit binary fractional field.100	 * Instead of calculating either one of101	 *102	 *    scaled_ppm = (ppb / 1000) << 16  [1]103	 *    scaled_ppm = (ppb << 16) / 1000  [2]104	 *105	 * we simply use double precision math, in order to avoid the106	 * truncation in [1] and the possible overflow in [2].107	 */108	return (long) (ppb * 65.536);109}110 111static int64_t pctns(struct ptp_clock_time *t)112{113	return t->sec * NSEC_PER_SEC + t->nsec;114}115 116static void usage(char *progname)117{118	fprintf(stderr,119		"usage: %s [options]\n"120		" -c         query the ptp clock's capabilities\n"121		" -d name    device to open\n"122		" -e val     read 'val' external time stamp events\n"123		" -f val     adjust the ptp clock frequency by 'val' ppb\n"124		" -F chan    Enable single channel mask and keep device open for debugfs verification.\n"125		" -g         get the ptp clock time\n"126		" -h         prints this message\n"127		" -i val     index for event/trigger\n"128		" -k val     measure the time offset between system and phc clock\n"129		"            for 'val' times (Maximum 25)\n"130		" -l         list the current pin configuration\n"131		" -L pin,val configure pin index 'pin' with function 'val'\n"132		"            the channel index is taken from the '-i' option\n"133		"            'val' specifies the auxiliary function:\n"134		"            0 - none\n"135		"            1 - external time stamp\n"136		"            2 - periodic output\n"137		" -n val     shift the ptp clock time by 'val' nanoseconds\n"138		" -o val     phase offset (in nanoseconds) to be provided to the PHC servo\n"139		" -p val     enable output with a period of 'val' nanoseconds\n"140		" -H val     set output phase to 'val' nanoseconds (requires -p)\n"141		" -w val     set output pulse width to 'val' nanoseconds (requires -p)\n"142		" -P val     enable or disable (val=1|0) the system clock PPS\n"143		" -s         set the ptp clock time from the system time\n"144		" -S         set the system time from the ptp clock time\n"145		" -t val     shift the ptp clock time by 'val' seconds\n"146		" -T val     set the ptp clock time to 'val' seconds\n"147		" -x val     get an extended ptp clock time with the desired number of samples (up to %d)\n"148		" -X         get a ptp clock cross timestamp\n"149		" -z         test combinations of rising/falling external time stamp flags\n",150		progname, PTP_MAX_SAMPLES);151}152 153int main(int argc, char *argv[])154{155	struct ptp_clock_caps caps;156	struct ptp_extts_event event;157	struct ptp_extts_request extts_request;158	struct ptp_perout_request perout_request;159	struct ptp_pin_desc desc;160	struct timespec ts;161	struct timex tx;162	struct ptp_clock_time *pct;163	struct ptp_sys_offset *sysoff;164	struct ptp_sys_offset_extended *soe;165	struct ptp_sys_offset_precise *xts;166 167	char *progname;168	unsigned int i;169	int c, cnt, fd;170 171	char *device = DEVICE;172	clockid_t clkid;173	int adjfreq = 0x7fffffff;174	int adjtime = 0;175	int adjns = 0;176	int adjphase = 0;177	int capabilities = 0;178	int extts = 0;179	int flagtest = 0;180	int gettime = 0;181	int index = 0;182	int list_pins = 0;183	int pct_offset = 0;184	int getextended = 0;185	int getcross = 0;186	int n_samples = 0;187	int pin_index = -1, pin_func;188	int pps = -1;189	int seconds = 0;190	int settime = 0;191	int channel = -1;192 193	int64_t t1, t2, tp;194	int64_t interval, offset;195	int64_t perout_phase = -1;196	int64_t pulsewidth = -1;197	int64_t perout = -1;198 199	progname = strrchr(argv[0], '/');200	progname = progname ? 1+progname : argv[0];201	while (EOF != (c = getopt(argc, argv, "cd:e:f:F:ghH:i:k:lL:n:o:p:P:sSt:T:w:x:Xz"))) {202		switch (c) {203		case 'c':204			capabilities = 1;205			break;206		case 'd':207			device = optarg;208			break;209		case 'e':210			extts = atoi(optarg);211			break;212		case 'f':213			adjfreq = atoi(optarg);214			break;215		case 'F':216			channel = atoi(optarg);217			break;218		case 'g':219			gettime = 1;220			break;221		case 'H':222			perout_phase = atoll(optarg);223			break;224		case 'i':225			index = atoi(optarg);226			break;227		case 'k':228			pct_offset = 1;229			n_samples = atoi(optarg);230			break;231		case 'l':232			list_pins = 1;233			break;234		case 'L':235			cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func);236			if (cnt != 2) {237				usage(progname);238				return -1;239			}240			break;241		case 'n':242			adjns = atoi(optarg);243			break;244		case 'o':245			adjphase = atoi(optarg);246			break;247		case 'p':248			perout = atoll(optarg);249			break;250		case 'P':251			pps = atoi(optarg);252			break;253		case 's':254			settime = 1;255			break;256		case 'S':257			settime = 2;258			break;259		case 't':260			adjtime = atoi(optarg);261			break;262		case 'T':263			settime = 3;264			seconds = atoi(optarg);265			break;266		case 'w':267			pulsewidth = atoi(optarg);268			break;269		case 'x':270			getextended = atoi(optarg);271			if (getextended < 1 || getextended > PTP_MAX_SAMPLES) {272				fprintf(stderr,273					"number of extended timestamp samples must be between 1 and %d; was asked for %d\n",274					PTP_MAX_SAMPLES, getextended);275				return -1;276			}277			break;278		case 'X':279			getcross = 1;280			break;281		case 'z':282			flagtest = 1;283			break;284		case 'h':285			usage(progname);286			return 0;287		case '?':288		default:289			usage(progname);290			return -1;291		}292	}293 294	fd = open(device, O_RDWR);295	if (fd < 0) {296		fprintf(stderr, "opening %s: %s\n", device, strerror(errno));297		return -1;298	}299 300	clkid = get_clockid(fd);301	if (CLOCK_INVALID == clkid) {302		fprintf(stderr, "failed to read clock id\n");303		return -1;304	}305 306	if (capabilities) {307		if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {308			perror("PTP_CLOCK_GETCAPS");309		} else {310			printf("capabilities:\n"311			       "  %d maximum frequency adjustment (ppb)\n"312			       "  %d programmable alarms\n"313			       "  %d external time stamp channels\n"314			       "  %d programmable periodic signals\n"315			       "  %d pulse per second\n"316			       "  %d programmable pins\n"317			       "  %d cross timestamping\n"318			       "  %d adjust_phase\n"319			       "  %d maximum phase adjustment (ns)\n",320			       caps.max_adj,321			       caps.n_alarm,322			       caps.n_ext_ts,323			       caps.n_per_out,324			       caps.pps,325			       caps.n_pins,326			       caps.cross_timestamping,327			       caps.adjust_phase,328			       caps.max_phase_adj);329		}330	}331 332	if (0x7fffffff != adjfreq) {333		memset(&tx, 0, sizeof(tx));334		tx.modes = ADJ_FREQUENCY;335		tx.freq = ppb_to_scaled_ppm(adjfreq);336		if (clock_adjtime(clkid, &tx)) {337			perror("clock_adjtime");338		} else {339			puts("frequency adjustment okay");340		}341	}342 343	if (adjtime || adjns) {344		memset(&tx, 0, sizeof(tx));345		tx.modes = ADJ_SETOFFSET | ADJ_NANO;346		tx.time.tv_sec = adjtime;347		tx.time.tv_usec = adjns;348		while (tx.time.tv_usec < 0) {349			tx.time.tv_sec  -= 1;350			tx.time.tv_usec += NSEC_PER_SEC;351		}352 353		if (clock_adjtime(clkid, &tx) < 0) {354			perror("clock_adjtime");355		} else {356			puts("time shift okay");357		}358	}359 360	if (adjphase) {361		memset(&tx, 0, sizeof(tx));362		tx.modes = ADJ_OFFSET | ADJ_NANO;363		tx.offset = adjphase;364 365		if (clock_adjtime(clkid, &tx) < 0) {366			perror("clock_adjtime");367		} else {368			puts("phase adjustment okay");369		}370	}371 372	if (gettime) {373		if (clock_gettime(clkid, &ts)) {374			perror("clock_gettime");375		} else {376			printf("clock time: %ld.%09ld or %s",377			       ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));378		}379	}380 381	if (settime == 1) {382		clock_gettime(CLOCK_REALTIME, &ts);383		if (clock_settime(clkid, &ts)) {384			perror("clock_settime");385		} else {386			puts("set time okay");387		}388	}389 390	if (settime == 2) {391		clock_gettime(clkid, &ts);392		if (clock_settime(CLOCK_REALTIME, &ts)) {393			perror("clock_settime");394		} else {395			puts("set time okay");396		}397	}398 399	if (settime == 3) {400		ts.tv_sec = seconds;401		ts.tv_nsec = 0;402		if (clock_settime(clkid, &ts)) {403			perror("clock_settime");404		} else {405			puts("set time okay");406		}407	}408 409	if (pin_index >= 0) {410		memset(&desc, 0, sizeof(desc));411		desc.index = pin_index;412		desc.func = pin_func;413		desc.chan = index;414		if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {415			perror("PTP_PIN_SETFUNC");416		} else {417			puts("set pin function okay");418		}419	}420 421	if (extts) {422		memset(&extts_request, 0, sizeof(extts_request));423		extts_request.index = index;424		extts_request.flags = PTP_ENABLE_FEATURE;425		if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {426			perror("PTP_EXTTS_REQUEST");427			extts = 0;428		} else {429			puts("external time stamp request okay");430		}431		for (; extts; extts--) {432			cnt = read(fd, &event, sizeof(event));433			if (cnt != sizeof(event)) {434				perror("read");435				break;436			}437			printf("event index %u at %lld.%09u\n", event.index,438			       event.t.sec, event.t.nsec);439			fflush(stdout);440		}441		/* Disable the feature again. */442		extts_request.flags = 0;443		if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {444			perror("PTP_EXTTS_REQUEST");445		}446	}447 448	if (flagtest) {449		do_flag_test(fd, index);450	}451 452	if (list_pins) {453		int n_pins = 0;454		if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {455			perror("PTP_CLOCK_GETCAPS");456		} else {457			n_pins = caps.n_pins;458		}459		for (i = 0; i < n_pins; i++) {460			desc.index = i;461			if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) {462				perror("PTP_PIN_GETFUNC");463				break;464			}465			printf("name %s index %u func %u chan %u\n",466			       desc.name, desc.index, desc.func, desc.chan);467		}468	}469 470	if (pulsewidth >= 0 && perout < 0) {471		puts("-w can only be specified together with -p");472		return -1;473	}474 475	if (perout_phase >= 0 && perout < 0) {476		puts("-H can only be specified together with -p");477		return -1;478	}479 480	if (perout >= 0) {481		if (clock_gettime(clkid, &ts)) {482			perror("clock_gettime");483			return -1;484		}485		memset(&perout_request, 0, sizeof(perout_request));486		perout_request.index = index;487		perout_request.period.sec = perout / NSEC_PER_SEC;488		perout_request.period.nsec = perout % NSEC_PER_SEC;489		perout_request.flags = 0;490		if (pulsewidth >= 0) {491			perout_request.flags |= PTP_PEROUT_DUTY_CYCLE;492			perout_request.on.sec = pulsewidth / NSEC_PER_SEC;493			perout_request.on.nsec = pulsewidth % NSEC_PER_SEC;494		}495		if (perout_phase >= 0) {496			perout_request.flags |= PTP_PEROUT_PHASE;497			perout_request.phase.sec = perout_phase / NSEC_PER_SEC;498			perout_request.phase.nsec = perout_phase % NSEC_PER_SEC;499		} else {500			perout_request.start.sec = ts.tv_sec + 2;501			perout_request.start.nsec = 0;502		}503 504		if (ioctl(fd, PTP_PEROUT_REQUEST2, &perout_request)) {505			perror("PTP_PEROUT_REQUEST");506		} else {507			puts("periodic output request okay");508		}509	}510 511	if (pps != -1) {512		int enable = pps ? 1 : 0;513		if (ioctl(fd, PTP_ENABLE_PPS, enable)) {514			perror("PTP_ENABLE_PPS");515		} else {516			puts("pps for system time request okay");517		}518	}519 520	if (pct_offset) {521		if (n_samples <= 0 || n_samples > 25) {522			puts("n_samples should be between 1 and 25");523			usage(progname);524			return -1;525		}526 527		sysoff = calloc(1, sizeof(*sysoff));528		if (!sysoff) {529			perror("calloc");530			return -1;531		}532		sysoff->n_samples = n_samples;533 534		if (ioctl(fd, PTP_SYS_OFFSET, sysoff))535			perror("PTP_SYS_OFFSET");536		else537			puts("system and phc clock time offset request okay");538 539		pct = &sysoff->ts[0];540		for (i = 0; i < sysoff->n_samples; i++) {541			t1 = pctns(pct+2*i);542			tp = pctns(pct+2*i+1);543			t2 = pctns(pct+2*i+2);544			interval = t2 - t1;545			offset = (t2 + t1) / 2 - tp;546 547			printf("system time: %lld.%09u\n",548				(pct+2*i)->sec, (pct+2*i)->nsec);549			printf("phc    time: %lld.%09u\n",550				(pct+2*i+1)->sec, (pct+2*i+1)->nsec);551			printf("system time: %lld.%09u\n",552				(pct+2*i+2)->sec, (pct+2*i+2)->nsec);553			printf("system/phc clock time offset is %" PRId64 " ns\n"554			       "system     clock time delay  is %" PRId64 " ns\n",555				offset, interval);556		}557 558		free(sysoff);559	}560 561	if (getextended) {562		soe = calloc(1, sizeof(*soe));563		if (!soe) {564			perror("calloc");565			return -1;566		}567 568		soe->n_samples = getextended;569 570		if (ioctl(fd, PTP_SYS_OFFSET_EXTENDED, soe)) {571			perror("PTP_SYS_OFFSET_EXTENDED");572		} else {573			printf("extended timestamp request returned %d samples\n",574			       getextended);575 576			for (i = 0; i < getextended; i++) {577				printf("sample #%2d: system time before: %lld.%09u\n",578				       i, soe->ts[i][0].sec, soe->ts[i][0].nsec);579				printf("            phc time: %lld.%09u\n",580				       soe->ts[i][1].sec, soe->ts[i][1].nsec);581				printf("            system time after: %lld.%09u\n",582				       soe->ts[i][2].sec, soe->ts[i][2].nsec);583			}584		}585 586		free(soe);587	}588 589	if (getcross) {590		xts = calloc(1, sizeof(*xts));591		if (!xts) {592			perror("calloc");593			return -1;594		}595 596		if (ioctl(fd, PTP_SYS_OFFSET_PRECISE, xts)) {597			perror("PTP_SYS_OFFSET_PRECISE");598		} else {599			puts("system and phc crosstimestamping request okay");600 601			printf("device time: %lld.%09u\n",602			       xts->device.sec, xts->device.nsec);603			printf("system time: %lld.%09u\n",604			       xts->sys_realtime.sec, xts->sys_realtime.nsec);605			printf("monoraw time: %lld.%09u\n",606			       xts->sys_monoraw.sec, xts->sys_monoraw.nsec);607		}608 609		free(xts);610	}611 612	if (channel >= 0) {613		if (ioctl(fd, PTP_MASK_CLEAR_ALL)) {614			perror("PTP_MASK_CLEAR_ALL");615		} else if (ioctl(fd, PTP_MASK_EN_SINGLE, (unsigned int *)&channel)) {616			perror("PTP_MASK_EN_SINGLE");617		} else {618			printf("Channel %d exclusively enabled. Check on debugfs.\n", channel);619			printf("Press any key to continue\n.");620			getchar();621		}622	}623 624	close(fd);625	return 0;626}627