1 /*********************************************************************** 2 * * 3 * Copyright (c) David L. Mills 1999-2009 * 4 * * 5 * Permission to use, copy, modify, and distribute this software and * 6 * its documentation for any purpose and with or without fee is hereby * 7 * granted, provided that the above copyright notice appears in all * 8 * copies and that both the copyright notice and this permission * 9 * notice appear in supporting documentation, and that the name * 10 * University of Delaware not be used in advertising or publicity * 11 * pertaining to distribution of the software without specific, * 12 * written prior permission. The University of Delaware makes no * 13 * representations about the suitability this software for any * 14 * purpose. It is provided "as is" without express or implied * 15 * warranty. * 16 * * 17 *********************************************************************** 18 * * 19 * This header file complies with "Pulse-Per-Second API for UNIX-like * 20 * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul * 21 * and Marc Brett, from whom much of this code was shamelessly stolen. * 22 * * 23 * this modified timepps.h can be used to provide a PPSAPI interface * 24 * to a machine running Solaris (2.6 and above). * 25 * * 26 *********************************************************************** 27 * * 28 * A full PPSAPI interface to the Solaris kernel would be better, but * 29 * this at least removes the necessity for special coding from the NTP * 30 * NTP drivers. * 31 * * 32 *********************************************************************** 33 * * 34 * Some of this include file * 35 * Copyright (c) 1999 by Ulrich Windl, * 36 * based on code by Reg Clemens <reg@dwf.com> * 37 * based on code by Poul-Henning Kamp <phk@FreeBSD.org> * 38 * * 39 *********************************************************************** 40 * * 41 * "THE BEER-WARE LICENSE" (Revision 42): * 42 * <phk@FreeBSD.org> wrote this file. As long as you retain this * 43 * notice you can do whatever you want with this stuff. If we meet some* 44 * day, and you think this stuff is worth it, you can buy me a beer * 45 * in return. Poul-Henning Kamp * 46 * * 47 **********************************************************************/ 48 49 /* Solaris version, TIOCGPPSEV and TIOCSPPS assumed to exist. */ 50 51 #ifndef _SYS_TIMEPPS_H_ 52 #define _SYS_TIMEPPS_H_ 53 54 #include <termios.h> /* to get TOCGPPSEV and TIOCSPPS */ 55 56 /* Implementation note: the logical states ``assert'' and ``clear'' 57 * are implemented in terms of the UART register, i.e. ``assert'' 58 * means the bit is set. 59 */ 60 61 /* 62 * The following definitions are architecture independent 63 */ 64 65 #define PPS_API_VERS_1 1 /* API version number */ 66 #define PPS_JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ 67 #define PPS_NANOSECOND 1000000000L /* one nanosecond in decimal */ 68 #define PPS_FRAC 4294967296. /* 2^32 as a double */ 69 70 #define PPS_NORMALIZE(x) /* normalize timespec */ \ 71 do { \ 72 if ((x).tv_nsec >= PPS_NANOSECOND) { \ 73 (x).tv_nsec -= PPS_NANOSECOND; \ 74 (x).tv_sec++; \ 75 } else if ((x).tv_nsec < 0) { \ 76 (x).tv_nsec += PPS_NANOSECOND; \ 77 (x).tv_sec--; \ 78 } \ 79 } while (0) 80 81 #define PPS_TSPECTONTP(x) /* convert timespec to l_fp */ \ 82 do { \ 83 double d_temp; \ 84 \ 85 (x).integral += (unsigned int)PPS_JAN_1970; \ 86 d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \ 87 if (d_temp >= PPS_FRAC) \ 88 (x).integral++; \ 89 (x).fractional = (unsigned int)d_temp; \ 90 } while (0) 91 92 /* 93 * Device/implementation parameters (mode) 94 */ 95 96 #define PPS_CAPTUREASSERT 0x01 /* capture assert events */ 97 #define PPS_CAPTURECLEAR 0x02 /* capture clear events */ 98 #define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ 99 100 #define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */ 101 #define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */ 102 #define PPS_OFFSETBOTH 0x30 /* apply compensation for both */ 103 104 #define PPS_CANWAIT 0x100 /* Can we wait for an event? */ 105 #define PPS_CANPOLL 0x200 /* "This bit is reserved for */ 106 107 /* 108 * Kernel actions (mode) 109 */ 110 111 #define PPS_ECHOASSERT 0x40 /* feed back assert event to output */ 112 #define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */ 113 114 /* 115 * Timestamp formats (tsformat) 116 */ 117 118 #define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */ 119 #define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */ 120 121 /* 122 * Kernel discipline actions (not used in Solaris) 123 */ 124 125 #define PPS_KC_HARDPPS 0 /* enable kernel consumer */ 126 #define PPS_KC_HARDPPS_PLL 1 /* phase-lock mode */ 127 #define PPS_KC_HARDPPS_FLL 2 /* frequency-lock mode */ 128 129 /* 130 * Type definitions 131 */ 132 133 typedef unsigned long pps_seq_t; /* sequence number */ 134 135 typedef struct ntp_fp { 136 unsigned int integral; 137 unsigned int fractional; 138 } ntp_fp_t; /* NTP-compatible time stamp */ 139 140 typedef union pps_timeu { /* timestamp format */ 141 struct timespec tspec; 142 ntp_fp_t ntpfp; 143 unsigned long longpad[3]; 144 } pps_timeu_t; /* generic data type to represent time stamps */ 145 146 /* 147 * Timestamp information structure 148 */ 149 150 typedef struct pps_info { 151 pps_seq_t assert_sequence; /* seq. num. of assert event */ 152 pps_seq_t clear_sequence; /* seq. num. of clear event */ 153 pps_timeu_t assert_tu; /* time of assert event */ 154 pps_timeu_t clear_tu; /* time of clear event */ 155 int current_mode; /* current mode bits */ 156 } pps_info_t; 157 158 #define assert_timestamp assert_tu.tspec 159 #define clear_timestamp clear_tu.tspec 160 161 #define assert_timestamp_ntpfp assert_tu.ntpfp 162 #define clear_timestamp_ntpfp clear_tu.ntpfp 163 164 /* 165 * Parameter structure 166 */ 167 168 typedef struct pps_params { 169 int api_version; /* API version # */ 170 int mode; /* mode bits */ 171 pps_timeu_t assert_off_tu; /* offset compensation for assert */ 172 pps_timeu_t clear_off_tu; /* offset compensation for clear */ 173 } pps_params_t; 174 175 #define assert_offset assert_off_tu.tspec 176 #define clear_offset clear_off_tu.tspec 177 178 #define assert_offset_ntpfp assert_off_tu.ntpfp 179 #define clear_offset_ntpfp clear_off_tu.ntpfp 180 181 /* addition of NTP fixed-point format */ 182 183 #define NTPFP_M_ADD(r_i, r_f, a_i, a_f) /* r += a */ \ 184 do { \ 185 register u_int32 lo_tmp; \ 186 register u_int32 hi_tmp; \ 187 \ 188 lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \ 189 hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \ 190 if (lo_tmp & 0x10000) \ 191 hi_tmp++; \ 192 (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \ 193 \ 194 (r_i) += (a_i); \ 195 if (hi_tmp & 0x10000) \ 196 (r_i)++; \ 197 } while (0) 198 199 #define NTPFP_L_ADDS(r, a) NTPFP_M_ADD((r)->integral, (r)->fractional, \ 200 (int)(a)->integral, (a)->fractional) 201 202 /* 203 * The following definitions are architecture-dependent 204 */ 205 206 #define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) 207 #define PPS_RO (PPS_CANWAIT | PPS_CANPOLL) 208 209 typedef struct { 210 int filedes; /* file descriptor */ 211 pps_params_t params; /* PPS parameters set by user */ 212 } pps_unit_t; 213 214 /* 215 *------ Here begins the implementation-specific part! ------ 216 */ 217 218 #include <errno.h> 219 220 /* 221 * pps handlebars, which are required to be an opaque scalar. This 222 * implementation uses the handle as a pointer so it must be large 223 * enough. uintptr_t is as large as a pointer. 224 */ 225 typedef uintptr_t pps_handle_t; 226 227 /* 228 * create PPS handle from file descriptor 229 */ 230 231 static inline int 232 time_pps_create( 233 int filedes, /* file descriptor */ 234 pps_handle_t *handle /* returned handle */ 235 ) 236 { 237 pps_unit_t *punit; 238 int one = 1; 239 240 /* 241 * Check for valid arguments and attach PPS signal. 242 */ 243 244 if (!handle) { 245 errno = EFAULT; 246 return (-1); /* null pointer */ 247 } 248 249 if (ioctl(filedes, TIOCSPPS, &one) < 0) { 250 perror("refclock_ioctl: TIOCSPPS failed:"); 251 return (-1); 252 } 253 254 /* 255 * Allocate and initialize default unit structure. 256 */ 257 258 punit = malloc(sizeof(*punit)); 259 if (NULL == punit) { 260 errno = ENOMEM; 261 return (-1); /* what, no memory? */ 262 } 263 264 memset(punit, 0, sizeof(*punit)); 265 punit->filedes = filedes; 266 punit->params.api_version = PPS_API_VERS_1; 267 punit->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC; 268 269 *handle = (pps_handle_t)punit; 270 return (0); 271 } 272 273 /* 274 * release PPS handle 275 */ 276 277 static inline int 278 time_pps_destroy( 279 pps_handle_t handle 280 ) 281 { 282 pps_unit_t *punit; 283 284 /* 285 * Check for valid arguments and detach PPS signal. 286 */ 287 288 if (!handle) { 289 errno = EBADF; 290 return (-1); /* bad handle */ 291 } 292 punit = (pps_unit_t *)handle; 293 free(punit); 294 return (0); 295 } 296 297 /* 298 * set parameters for handle 299 */ 300 301 static inline int 302 time_pps_setparams( 303 pps_handle_t handle, 304 const pps_params_t *params 305 ) 306 { 307 pps_unit_t * punit; 308 int mode, mode_in; 309 /* 310 * Check for valid arguments and set parameters. 311 */ 312 313 if (!handle) { 314 errno = EBADF; 315 return (-1); /* bad handle */ 316 } 317 318 if (!params) { 319 errno = EFAULT; 320 return (-1); /* bad argument */ 321 } 322 323 /* 324 * There was no reasonable consensu in the API working group. 325 * I require `api_version' to be set! 326 */ 327 328 if (params->api_version != PPS_API_VERS_1) { 329 errno = EINVAL; 330 return(-1); 331 } 332 333 /* 334 * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT 335 */ 336 337 mode_in = params->mode; 338 punit = (pps_unit_t *)handle; 339 340 /* 341 * Only one of the time formats may be selected 342 * if a nonzero assert offset is supplied. 343 */ 344 if ((mode_in & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 345 (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) { 346 347 if (punit->params.assert_offset.tv_sec || 348 punit->params.assert_offset.tv_nsec) { 349 350 errno = EINVAL; 351 return(-1); 352 } 353 354 /* 355 * If no offset was specified but both time 356 * format flags are used consider it harmless 357 * but turn off PPS_TSFMT_NTPFP so getparams 358 * will not show both formats lit. 359 */ 360 mode_in &= ~PPS_TSFMT_NTPFP; 361 } 362 363 /* turn off read-only bits */ 364 365 mode_in &= ~PPS_RO; 366 367 /* 368 * test remaining bits, should only have captureassert, 369 * offsetassert, and/or timestamp format bits. 370 */ 371 372 if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT | 373 PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) { 374 errno = EOPNOTSUPP; 375 return(-1); 376 } 377 378 /* 379 * ok, ready to go. 380 */ 381 382 mode = punit->params.mode; 383 memcpy(&punit->params, params, sizeof(punit->params)); 384 punit->params.api_version = PPS_API_VERS_1; 385 punit->params.mode = mode | mode_in; 386 return (0); 387 } 388 389 /* 390 * get parameters for handle 391 */ 392 393 static inline int 394 time_pps_getparams( 395 pps_handle_t handle, 396 pps_params_t *params 397 ) 398 { 399 pps_unit_t * punit; 400 401 /* 402 * Check for valid arguments and get parameters. 403 */ 404 405 if (!handle) { 406 errno = EBADF; 407 return (-1); /* bad handle */ 408 } 409 410 if (!params) { 411 errno = EFAULT; 412 return (-1); /* bad argument */ 413 } 414 415 punit = (pps_unit_t *)handle; 416 memcpy(params, &punit->params, sizeof(*params)); 417 return (0); 418 } 419 420 /* 421 * get capabilities for handle 422 */ 423 424 static inline int 425 time_pps_getcap( 426 pps_handle_t handle, 427 int *mode 428 ) 429 { 430 /* 431 * Check for valid arguments and get capabilities. 432 */ 433 434 if (!handle) { 435 errno = EBADF; 436 return (-1); /* bad handle */ 437 } 438 439 if (!mode) { 440 errno = EFAULT; 441 return (-1); /* bad argument */ 442 } 443 *mode = PPS_CAP; 444 return (0); 445 } 446 447 /* 448 * Fetch timestamps 449 */ 450 451 static inline int 452 time_pps_fetch( 453 pps_handle_t handle, 454 const int tsformat, 455 pps_info_t *ppsinfo, 456 const struct timespec *timeout 457 ) 458 { 459 struct ppsclockev { 460 struct timeval tv; 461 u_int serial; 462 } ev; 463 464 pps_info_t infobuf; 465 pps_unit_t * punit; 466 467 /* 468 * Check for valid arguments and fetch timestamps 469 */ 470 471 if (!handle) { 472 errno = EBADF; 473 return (-1); /* bad handle */ 474 } 475 476 if (!ppsinfo) { 477 errno = EFAULT; 478 return (-1); /* bad argument */ 479 } 480 481 /* 482 * nb. PPS_CANWAIT is NOT set by the implementation, we can totally 483 * ignore the timeout variable. 484 */ 485 486 memset(&infobuf, 0, sizeof(infobuf)); 487 punit = (pps_unit_t *)handle; 488 489 /* 490 * if not captureassert, nothing to return. 491 */ 492 493 if (!punit->params.mode & PPS_CAPTUREASSERT) { 494 memcpy(ppsinfo, &infobuf, sizeof(*ppsinfo)); 495 return (0); 496 } 497 498 if (ioctl(punit->filedes, TIOCGPPSEV, (caddr_t) &ev) < 0) { 499 perror("time_pps_fetch:"); 500 errno = EOPNOTSUPP; 501 return(-1); 502 } 503 504 infobuf.assert_sequence = ev.serial; 505 infobuf.assert_timestamp.tv_sec = ev.tv.tv_sec; 506 infobuf.assert_timestamp.tv_nsec = ev.tv.tv_usec * 1000; 507 508 /* 509 * Translate to specified format then apply offset 510 */ 511 512 switch (tsformat) { 513 case PPS_TSFMT_TSPEC: 514 /* timespec format requires no conversion */ 515 if (punit->params.mode & PPS_OFFSETASSERT) { 516 infobuf.assert_timestamp.tv_sec += 517 punit->params.assert_offset.tv_sec; 518 infobuf.assert_timestamp.tv_nsec += 519 punit->params.assert_offset.tv_nsec; 520 PPS_NORMALIZE(infobuf.assert_timestamp); 521 } 522 break; 523 524 case PPS_TSFMT_NTPFP: 525 /* NTP format requires conversion to fraction form */ 526 PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp); 527 if (punit->params.mode & PPS_OFFSETASSERT) 528 NTPFP_L_ADDS(&infobuf.assert_timestamp_ntpfp, 529 &punit->params.assert_offset_ntpfp); 530 break; 531 532 default: 533 errno = EINVAL; 534 return (-1); 535 } 536 537 infobuf.current_mode = punit->params.mode; 538 memcpy(ppsinfo, &infobuf, sizeof(*ppsinfo)); 539 return (0); 540 } 541 542 /* 543 * specify kernel consumer 544 */ 545 546 static inline int 547 time_pps_kcbind( 548 pps_handle_t handle, 549 const int kernel_consumer, 550 const int edge, 551 const int tsformat 552 ) 553 { 554 /* 555 * Check for valid arguments and bind kernel consumer 556 */ 557 if (!handle) { 558 errno = EBADF; 559 return (-1); /* bad handle */ 560 } 561 if (geteuid() != 0) { 562 errno = EPERM; 563 return (-1); /* must be superuser */ 564 } 565 errno = EOPNOTSUPP; 566 return(-1); 567 } 568 569 #endif /* _SYS_TIMEPPS_H_ */ 570