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 #define _GNU_SOURCE 21 #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <inttypes.h> 25 #include <math.h> 26 #include <signal.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <sys/ioctl.h> 31 #include <sys/mman.h> 32 #include <sys/stat.h> 33 #include <sys/time.h> 34 #include <sys/timex.h> 35 #include <sys/types.h> 36 #include <time.h> 37 #include <unistd.h> 38 39 #include <linux/ptp_clock.h> 40 41 #define DEVICE "/dev/ptp0" 42 43 #ifndef ADJ_SETOFFSET 44 #define ADJ_SETOFFSET 0x0100 45 #endif 46 47 #ifndef CLOCK_INVALID 48 #define CLOCK_INVALID -1 49 #endif 50 51 /* clock_adjtime is not available in GLIBC < 2.14 */ 52 #if !__GLIBC_PREREQ(2, 14) 53 #include <sys/syscall.h> 54 static int clock_adjtime(clockid_t id, struct timex *tx) 55 { 56 return syscall(__NR_clock_adjtime, id, tx); 57 } 58 #endif 59 60 static clockid_t get_clockid(int fd) 61 { 62 #define CLOCKFD 3 63 return (((unsigned int) ~fd) << 3) | CLOCKFD; 64 } 65 66 static long ppb_to_scaled_ppm(int ppb) 67 { 68 /* 69 * The 'freq' field in the 'struct timex' is in parts per 70 * million, but with a 16 bit binary fractional field. 71 * Instead of calculating either one of 72 * 73 * scaled_ppm = (ppb / 1000) << 16 [1] 74 * scaled_ppm = (ppb << 16) / 1000 [2] 75 * 76 * we simply use double precision math, in order to avoid the 77 * truncation in [1] and the possible overflow in [2]. 78 */ 79 return (long) (ppb * 65.536); 80 } 81 82 static int64_t pctns(struct ptp_clock_time *t) 83 { 84 return t->sec * 1000000000LL + t->nsec; 85 } 86 87 static void usage(char *progname) 88 { 89 fprintf(stderr, 90 "usage: %s [options]\n" 91 " -c query the ptp clock's capabilities\n" 92 " -d name device to open\n" 93 " -e val read 'val' external time stamp events\n" 94 " -f val adjust the ptp clock frequency by 'val' ppb\n" 95 " -g get the ptp clock time\n" 96 " -h prints this message\n" 97 " -i val index for event/trigger\n" 98 " -k val measure the time offset between system and phc clock\n" 99 " for 'val' times (Maximum 25)\n" 100 " -l list the current pin configuration\n" 101 " -L pin,val configure pin index 'pin' with function 'val'\n" 102 " the channel index is taken from the '-i' option\n" 103 " 'val' specifies the auxiliary function:\n" 104 " 0 - none\n" 105 " 1 - external time stamp\n" 106 " 2 - periodic output\n" 107 " -p val enable output with a period of 'val' nanoseconds\n" 108 " -P val enable or disable (val=1|0) the system clock PPS\n" 109 " -s set the ptp clock time from the system time\n" 110 " -S set the system time from the ptp clock time\n" 111 " -t val shift the ptp clock time by 'val' seconds\n" 112 " -T val set the ptp clock time to 'val' seconds\n", 113 progname); 114 } 115 116 int main(int argc, char *argv[]) 117 { 118 struct ptp_clock_caps caps; 119 struct ptp_extts_event event; 120 struct ptp_extts_request extts_request; 121 struct ptp_perout_request perout_request; 122 struct ptp_pin_desc desc; 123 struct timespec ts; 124 struct timex tx; 125 struct ptp_clock_time *pct; 126 struct ptp_sys_offset *sysoff; 127 128 char *progname; 129 unsigned int i; 130 int c, cnt, fd; 131 132 char *device = DEVICE; 133 clockid_t clkid; 134 int adjfreq = 0x7fffffff; 135 int adjtime = 0; 136 int capabilities = 0; 137 int extts = 0; 138 int gettime = 0; 139 int index = 0; 140 int list_pins = 0; 141 int pct_offset = 0; 142 int n_samples = 0; 143 int perout = -1; 144 int pin_index = -1, pin_func; 145 int pps = -1; 146 int seconds = 0; 147 int settime = 0; 148 149 int64_t t1, t2, tp; 150 int64_t interval, offset; 151 152 progname = strrchr(argv[0], '/'); 153 progname = progname ? 1+progname : argv[0]; 154 while (EOF != (c = getopt(argc, argv, "cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) { 155 switch (c) { 156 case 'c': 157 capabilities = 1; 158 break; 159 case 'd': 160 device = optarg; 161 break; 162 case 'e': 163 extts = atoi(optarg); 164 break; 165 case 'f': 166 adjfreq = atoi(optarg); 167 break; 168 case 'g': 169 gettime = 1; 170 break; 171 case 'i': 172 index = atoi(optarg); 173 break; 174 case 'k': 175 pct_offset = 1; 176 n_samples = atoi(optarg); 177 break; 178 case 'l': 179 list_pins = 1; 180 break; 181 case 'L': 182 cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func); 183 if (cnt != 2) { 184 usage(progname); 185 return -1; 186 } 187 break; 188 case 'p': 189 perout = atoi(optarg); 190 break; 191 case 'P': 192 pps = atoi(optarg); 193 break; 194 case 's': 195 settime = 1; 196 break; 197 case 'S': 198 settime = 2; 199 break; 200 case 't': 201 adjtime = atoi(optarg); 202 break; 203 case 'T': 204 settime = 3; 205 seconds = atoi(optarg); 206 break; 207 case 'h': 208 usage(progname); 209 return 0; 210 case '?': 211 default: 212 usage(progname); 213 return -1; 214 } 215 } 216 217 fd = open(device, O_RDWR); 218 if (fd < 0) { 219 fprintf(stderr, "opening %s: %s\n", device, strerror(errno)); 220 return -1; 221 } 222 223 clkid = get_clockid(fd); 224 if (CLOCK_INVALID == clkid) { 225 fprintf(stderr, "failed to read clock id\n"); 226 return -1; 227 } 228 229 if (capabilities) { 230 if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) { 231 perror("PTP_CLOCK_GETCAPS"); 232 } else { 233 printf("capabilities:\n" 234 " %d maximum frequency adjustment (ppb)\n" 235 " %d programmable alarms\n" 236 " %d external time stamp channels\n" 237 " %d programmable periodic signals\n" 238 " %d pulse per second\n" 239 " %d programmable pins\n" 240 " %d cross timestamping\n", 241 caps.max_adj, 242 caps.n_alarm, 243 caps.n_ext_ts, 244 caps.n_per_out, 245 caps.pps, 246 caps.n_pins, 247 caps.cross_timestamping); 248 } 249 } 250 251 if (0x7fffffff != adjfreq) { 252 memset(&tx, 0, sizeof(tx)); 253 tx.modes = ADJ_FREQUENCY; 254 tx.freq = ppb_to_scaled_ppm(adjfreq); 255 if (clock_adjtime(clkid, &tx)) { 256 perror("clock_adjtime"); 257 } else { 258 puts("frequency adjustment okay"); 259 } 260 } 261 262 if (adjtime) { 263 memset(&tx, 0, sizeof(tx)); 264 tx.modes = ADJ_SETOFFSET; 265 tx.time.tv_sec = adjtime; 266 tx.time.tv_usec = 0; 267 if (clock_adjtime(clkid, &tx) < 0) { 268 perror("clock_adjtime"); 269 } else { 270 puts("time shift okay"); 271 } 272 } 273 274 if (gettime) { 275 if (clock_gettime(clkid, &ts)) { 276 perror("clock_gettime"); 277 } else { 278 printf("clock time: %ld.%09ld or %s", 279 ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec)); 280 } 281 } 282 283 if (settime == 1) { 284 clock_gettime(CLOCK_REALTIME, &ts); 285 if (clock_settime(clkid, &ts)) { 286 perror("clock_settime"); 287 } else { 288 puts("set time okay"); 289 } 290 } 291 292 if (settime == 2) { 293 clock_gettime(clkid, &ts); 294 if (clock_settime(CLOCK_REALTIME, &ts)) { 295 perror("clock_settime"); 296 } else { 297 puts("set time okay"); 298 } 299 } 300 301 if (settime == 3) { 302 ts.tv_sec = seconds; 303 ts.tv_nsec = 0; 304 if (clock_settime(clkid, &ts)) { 305 perror("clock_settime"); 306 } else { 307 puts("set time okay"); 308 } 309 } 310 311 if (extts) { 312 memset(&extts_request, 0, sizeof(extts_request)); 313 extts_request.index = index; 314 extts_request.flags = PTP_ENABLE_FEATURE; 315 if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) { 316 perror("PTP_EXTTS_REQUEST"); 317 extts = 0; 318 } else { 319 puts("external time stamp request okay"); 320 } 321 for (; extts; extts--) { 322 cnt = read(fd, &event, sizeof(event)); 323 if (cnt != sizeof(event)) { 324 perror("read"); 325 break; 326 } 327 printf("event index %u at %lld.%09u\n", event.index, 328 event.t.sec, event.t.nsec); 329 fflush(stdout); 330 } 331 /* Disable the feature again. */ 332 extts_request.flags = 0; 333 if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) { 334 perror("PTP_EXTTS_REQUEST"); 335 } 336 } 337 338 if (list_pins) { 339 int n_pins = 0; 340 if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) { 341 perror("PTP_CLOCK_GETCAPS"); 342 } else { 343 n_pins = caps.n_pins; 344 } 345 for (i = 0; i < n_pins; i++) { 346 desc.index = i; 347 if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) { 348 perror("PTP_PIN_GETFUNC"); 349 break; 350 } 351 printf("name %s index %u func %u chan %u\n", 352 desc.name, desc.index, desc.func, desc.chan); 353 } 354 } 355 356 if (perout >= 0) { 357 if (clock_gettime(clkid, &ts)) { 358 perror("clock_gettime"); 359 return -1; 360 } 361 memset(&perout_request, 0, sizeof(perout_request)); 362 perout_request.index = index; 363 perout_request.start.sec = ts.tv_sec + 2; 364 perout_request.start.nsec = 0; 365 perout_request.period.sec = 0; 366 perout_request.period.nsec = perout; 367 if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) { 368 perror("PTP_PEROUT_REQUEST"); 369 } else { 370 puts("periodic output request okay"); 371 } 372 } 373 374 if (pin_index >= 0) { 375 memset(&desc, 0, sizeof(desc)); 376 desc.index = pin_index; 377 desc.func = pin_func; 378 desc.chan = index; 379 if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) { 380 perror("PTP_PIN_SETFUNC"); 381 } else { 382 puts("set pin function okay"); 383 } 384 } 385 386 if (pps != -1) { 387 int enable = pps ? 1 : 0; 388 if (ioctl(fd, PTP_ENABLE_PPS, enable)) { 389 perror("PTP_ENABLE_PPS"); 390 } else { 391 puts("pps for system time request okay"); 392 } 393 } 394 395 if (pct_offset) { 396 if (n_samples <= 0 || n_samples > 25) { 397 puts("n_samples should be between 1 and 25"); 398 usage(progname); 399 return -1; 400 } 401 402 sysoff = calloc(1, sizeof(*sysoff)); 403 if (!sysoff) { 404 perror("calloc"); 405 return -1; 406 } 407 sysoff->n_samples = n_samples; 408 409 if (ioctl(fd, PTP_SYS_OFFSET, sysoff)) 410 perror("PTP_SYS_OFFSET"); 411 else 412 puts("system and phc clock time offset request okay"); 413 414 pct = &sysoff->ts[0]; 415 for (i = 0; i < sysoff->n_samples; i++) { 416 t1 = pctns(pct+2*i); 417 tp = pctns(pct+2*i+1); 418 t2 = pctns(pct+2*i+2); 419 interval = t2 - t1; 420 offset = (t2 + t1) / 2 - tp; 421 422 printf("system time: %lld.%u\n", 423 (pct+2*i)->sec, (pct+2*i)->nsec); 424 printf("phc time: %lld.%u\n", 425 (pct+2*i+1)->sec, (pct+2*i+1)->nsec); 426 printf("system time: %lld.%u\n", 427 (pct+2*i+2)->sec, (pct+2*i+2)->nsec); 428 printf("system/phc clock time offset is %" PRId64 " ns\n" 429 "system clock time delay is %" PRId64 " ns\n", 430 offset, interval); 431 } 432 433 free(sysoff); 434 } 435 436 close(fd); 437 return 0; 438 } 439