root/tools/testing/selftests/ptp/testptp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clock_adjtime
  2. show_flag_test
  3. do_flag_test
  4. get_clockid
  5. ppb_to_scaled_ppm
  6. pctns
  7. usage
  8. main

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

/* [<][>][^][v][top][bottom][index][help] */