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