Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.8-rc3 381 lines 8.9 kB view raw
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}